From 8e150f7fc265c26d38ceb65941a08749e9ec8d00 Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:18:59 +0200 Subject: [PATCH 01/48] build local docker image --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 53592cb..91a7401 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,10 @@ test: docker: $(MAKE) -C lean_client docker +.PHONY: docker-local +docker-local: + $(MAKE) -C lean_client docker-local + .PHONY: release release: $(MAKE) -C lean_client release From e6646cdf874ffb5266576e219860175b1586614e Mon Sep 17 00:00:00 2001 From: LiudasBaronas1 <144480589+LiudasBaronas1@users.noreply.github.com> Date: Mon, 12 Jan 2026 01:16:22 +0200 Subject: [PATCH 02/48] Refactor: remove redundant constants from config, implement ChainConfig::devnet() and update lib exports --- lean_client/chain/src/config.rs | 71 ++++++++++++--------------------- lean_client/chain/src/lib.rs | 3 +- 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/lean_client/chain/src/config.rs b/lean_client/chain/src/config.rs index ca4edb2..71b4df1 100644 --- a/lean_client/chain/src/config.rs +++ b/lean_client/chain/src/config.rs @@ -3,6 +3,7 @@ pub struct BasisPoint(pub u64); impl BasisPoint { pub const MAX: u64 = 10_000; + pub const fn new(value: u64) -> Option { if value <= Self::MAX { Some(BasisPoint(value)) @@ -14,38 +15,19 @@ impl BasisPoint { pub fn get(&self) -> u64 { self.0 } -} - -pub const INTERVALS_PER_SLOT: u64 = 4; -pub const SLOT_DURATION_MS: u64 = 4_000; -pub const SECONDS_PER_SLOT: u64 = SLOT_DURATION_MS / 1_000; -pub const SECONDS_PER_INTERVAL: u64 = SECONDS_PER_SLOT / INTERVALS_PER_SLOT; -pub const JUSTIFICATION_LOOKBACK_SLOTS: u64 = 3; -pub const PROPOSER_REORG_CUTOFF_BPS: BasisPoint = match BasisPoint::new(2_500) { - Some(x) => x, - None => panic!(), -}; -pub const VOTE_DUE_BPS: BasisPoint = match BasisPoint::new(5_000) { - Some(x) => x, - None => panic!(), -}; -pub const FAST_CONFIRM_DUE_BPS: BasisPoint = match BasisPoint::new(7_500) { - Some(x) => x, - None => panic!(), -}; -pub const VIEW_FREEZE_CUTOFF_BPS: BasisPoint = match BasisPoint::new(7_500) { - Some(x) => x, - None => panic!(), -}; - -pub const HISTORICAL_ROOTS_LIMIT: u64 = 1u64 << 18; -pub const VALIDATOR_REGISTRY_LIMIT: u64 = 1u64 << 12; + #[inline] + pub fn get(&self) -> u64 { + self.0 + } +} #[derive(Clone, Debug)] pub struct ChainConfig { + pub intervals_per_slot: u64, pub slot_duration_ms: u64, pub second_per_slot: u64, + pub seconds_per_interval: u64, pub justification_lookback_slots: u64, pub proposer_reorg_cutoff_bps: BasisPoint, pub vote_due_bps: BasisPoint, @@ -55,25 +37,24 @@ pub struct ChainConfig { pub validator_registry_limit: u64, } -pub const DEVNET_CONFIG: ChainConfig = ChainConfig { - slot_duration_ms: SLOT_DURATION_MS, - second_per_slot: SECONDS_PER_SLOT, - justification_lookback_slots: JUSTIFICATION_LOOKBACK_SLOTS, - proposer_reorg_cutoff_bps: PROPOSER_REORG_CUTOFF_BPS, - vote_due_bps: VOTE_DUE_BPS, - fast_confirm_due_bps: FAST_CONFIRM_DUE_BPS, - view_freeze_cutoff_bps: VIEW_FREEZE_CUTOFF_BPS, - historical_roots_limit: HISTORICAL_ROOTS_LIMIT, - validator_registry_limit: VALIDATOR_REGISTRY_LIMIT, -}; +impl ChainConfig { + pub fn devnet() -> Self { + let slot_duration_ms = 4_000; + let seconds_per_slot = slot_duration_ms / 1_000; + let intervals_per_slot = 4; -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn time_math_is_consistent() { - assert_eq!(SLOT_DURATION_MS, 4_000); - assert_eq!(SECONDS_PER_SLOT, 4); - assert_eq!(SECONDS_PER_INTERVAL, 1); + Self { + slot_duration_ms, + second_per_slot: seconds_per_slot, + intervals_per_slot, + seconds_per_interval: seconds_per_slot / intervals_per_slot, + justification_lookback_slots: 3, + proposer_reorg_cutoff_bps: BasisPoint::new(2_500).expect("Valid BPS"), + vote_due_bps: BasisPoint::new(5_000).expect("Valid BPS"), + fast_confirm_due_bps: BasisPoint::new(7_500).expect("Valid BPS"), + view_freeze_cutoff_bps: BasisPoint::new(7_500).expect("Valid BPS"), + historical_roots_limit: 1u64 << 18, + validator_registry_limit: 1u64 << 12, + } } } diff --git a/lean_client/chain/src/lib.rs b/lean_client/chain/src/lib.rs index ef68c36..12cf630 100644 --- a/lean_client/chain/src/lib.rs +++ b/lean_client/chain/src/lib.rs @@ -1 +1,2 @@ -pub mod config; +mod config; +pub use config::ChainConfig; \ No newline at end of file From c32bb801c81fb84f45466066fc1171e7bc3dcce9 Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Fri, 19 Dec 2025 00:46:02 +0200 Subject: [PATCH 03/48] prepared container types for signature aggregation (devnet 2) --- lean_client/containers/Cargo.toml | 3 ++ lean_client/containers/src/attestation.rs | 20 +++++++----- lean_client/containers/src/block.rs | 19 ++++++++++-- lean_client/containers/src/lib.rs | 4 +-- lean_client/containers/src/serde_helpers.rs | 34 +++++++++++++++++++-- lean_client/containers/src/state.rs | 34 ++++++++++++++++++--- lean_client/src/main.rs | 6 ++-- 7 files changed, 96 insertions(+), 24 deletions(-) diff --git a/lean_client/containers/Cargo.toml b/lean_client/containers/Cargo.toml index 2d5b0ff..e5aa52f 100644 --- a/lean_client/containers/Cargo.toml +++ b/lean_client/containers/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" [features] xmss-verify = ["leansig"] +default = ["devnet1"] +devnet1 = [] +devnet2 = [] [lib] name = "containers" diff --git a/lean_client/containers/src/attestation.rs b/lean_client/containers/src/attestation.rs index ae1b88c..e96a595 100644 --- a/lean_client/containers/src/attestation.rs +++ b/lean_client/containers/src/attestation.rs @@ -19,9 +19,9 @@ use typenum::U4096; /// Limit is VALIDATOR_REGISTRY_LIMIT (4096). pub type Attestations = ssz::PersistentList; -/// List of signatures corresponding to attestations in a block. -/// Limit is VALIDATOR_REGISTRY_LIMIT (4096). -pub type BlockSignatures = ssz::PersistentList; +pub type AggregatedAttestations = ssz::PersistentList; + +pub type AttestationSignatures = ssz::PersistentList; /// Bitlist representing validator participation in an attestation. /// Limit is VALIDATOR_REGISTRY_LIMIT (4096). @@ -57,15 +57,19 @@ pub struct Attestation { /// Validator attestation bundled with its signature. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct SignedAttestation { - /// The attestation message signed by the validator. + #[cfg(feature = "devnet2")] + pub validator_id: u64, + #[cfg(feature = "devnet2")] + pub message: AttestationData, + #[cfg(feature = "devnet1")] pub message: Attestation, - /// Signature aggregation produced by the leanVM (SNARKs in the future). + /// signature over attestaion message only as it would be aggregated later in attestation pub signature: Signature, } /// Aggregated attestation consisting of participation bits and message. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] -pub struct AggregatedAttestations { +pub struct AggregatedAttestation { /// Bitfield indicating which validators participated in the aggregation. pub aggregation_bits: AggregationBits, /// Combined attestation data similar to the beacon chain format. @@ -77,9 +81,9 @@ pub struct AggregatedAttestations { /// Aggregated attestation bundled with aggregated signatures. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] -pub struct SignedAggregatedAttestations { +pub struct SignedAggregatedAttestation { /// Aggregated attestation data. - pub message: AggregatedAttestations, + pub message: AggregatedAttestation, /// Aggregated attestation plus its combined signature. /// /// Stores a naive list of validator signatures that mirrors the attestation diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index e4cc998..832d2d8 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -1,11 +1,12 @@ -use crate::{ - Attestation, Attestations, BlockSignatures, Bytes32, Signature, Slot, State, ValidatorIndex, -}; +use crate::{Attestation, Attestations, Bytes32, Signature, Slot, State, ValidatorIndex}; use serde::{Deserialize, Serialize}; use ssz_derive::Ssz; #[cfg(feature = "xmss-verify")] use leansig::signature::generalized_xmss::instantiations_poseidon::lifetime_2_to_the_20::target_sum::SIGTargetSumLifetime20W2NoOff; +use ssz::PersistentList; +use typenum::U4096; +use crate::attestation::AttestationSignatures; /// The body of a block, containing payload data. /// @@ -13,6 +14,9 @@ use leansig::signature::generalized_xmss::instantiations_poseidon::lifetime_2_to /// separately in BlockSignatures to match the spec architecture. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct BlockBody { + #[cfg(feature = "devnet2")] + pub attestations: VariableList, + #[cfg(feature = "devnet1")] #[serde(with = "crate::serde_helpers")] pub attestations: Attestations, } @@ -47,6 +51,12 @@ pub struct BlockWithAttestation { pub proposer_attestation: Attestation, } +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)] +pub struct BlockSignatures { + pub attestation_signatures: AttestationSignatures, + pub proposer_signature: Signature, +} + /// Envelope carrying a block, an attestation from proposer, and aggregated signatures. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -56,7 +66,10 @@ pub struct SignedBlockWithAttestation { /// Aggregated signature payload for the block. /// /// Signatures remain in attestation order followed by the proposer signature. + #[cfg(feature = "devnet1")] #[serde(with = "crate::serde_helpers::block_signatures")] + pub signature: PersistentList, + #[cfg(feature = "devnet2")] pub signature: BlockSignatures, } diff --git a/lean_client/containers/src/lib.rs b/lean_client/containers/src/lib.rs index 28b13d1..f0590ca 100644 --- a/lean_client/containers/src/lib.rs +++ b/lean_client/containers/src/lib.rs @@ -10,8 +10,8 @@ pub mod types; pub mod validator; pub use attestation::{ - AggregatedAttestations, AggregatedSignatures, AggregationBits, Attestation, AttestationData, - Attestations, BlockSignatures, Signature, SignedAggregatedAttestations, SignedAttestation, + AggregatedAttestation, AggregatedSignatures, AggregationBits, Attestation, AttestationData, + Attestations, Signature, SignedAggregatedAttestation, SignedAttestation, }; pub use block::{ Block, BlockBody, BlockHeader, BlockWithAttestation, SignedBlock, SignedBlockWithAttestation, diff --git a/lean_client/containers/src/serde_helpers.rs b/lean_client/containers/src/serde_helpers.rs index 7cff787..0568f71 100644 --- a/lean_client/containers/src/serde_helpers.rs +++ b/lean_client/containers/src/serde_helpers.rs @@ -187,9 +187,10 @@ pub mod signature { /// where each signature can be either hex string or structured XMSS format pub mod block_signatures { use super::*; - use crate::{BlockSignatures, Signature}; + use crate::Signature; use serde_json::Value; use ssz::PersistentList; + use typenum::U4096; /// Structured XMSS signature format from test vectors #[derive(Deserialize, Clone)] @@ -247,7 +248,10 @@ pub mod block_signatures { Signature::try_from(bytes.as_slice()).map_err(|_| "Failed to create signature".to_string()) } - pub fn deserialize<'de, D>(deserializer: D) -> Result + #[cfg(feature = "devnet1")] + pub fn deserialize<'de, D>( + deserializer: D, + ) -> Result, D::Error> where D: Deserializer<'de>, { @@ -269,7 +273,21 @@ pub mod block_signatures { Ok(signatures) } - pub fn serialize(value: &BlockSignatures, serializer: S) -> Result + #[cfg(feature = "devnet2")] + pub fn deserialize<'de, D>(_: D) -> Result + where + D: Deserializer<'de>, + { + Err(serde::de::Error::custom( + "BlockSignatures deserialization not implemented for devnet2", + )) + } + + #[cfg(feature = "devnet1")] + pub fn serialize( + value: &PersistentList, + serializer: S, + ) -> Result where S: Serializer, { @@ -289,4 +307,14 @@ pub mod block_signatures { let wrapper = DataWrapper { data: sigs }; wrapper.serialize(serializer) } + + #[cfg(feature = "devnet2")] + pub fn serialize(value: &BlockSignatures, serializer: S) -> Result + where + S: Serializer, + { + Err(serde::de::Error::custom( + "BlockSignatures serialization not implemented for devnet2", + )) + } } diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index 2dccbf3..bd36e33 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -1,16 +1,17 @@ use crate::validator::Validator; use crate::{ block::{hash_tree_root, Block, BlockBody, BlockHeader, SignedBlockWithAttestation}, - Attestation, Attestations, BlockSignatures, Bytes32, Checkpoint, Config, Slot, Uint64, + Attestation, Attestations, Bytes32, Checkpoint, Config, Signature, Slot, Uint64, ValidatorIndex, }; use crate::{ HistoricalBlockHashes, JustificationRoots, JustificationsValidators, JustifiedSlots, Validators, }; use serde::{Deserialize, Serialize}; -use ssz::PersistentList as List; +use ssz::{PersistentList as List, PersistentList}; use ssz_derive::Ssz; use std::collections::BTreeMap; +use typenum::U4096; pub const VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 pub const JUSTIFICATION_ROOTS_LIMIT: usize = 1 << 18; // 262144 @@ -529,6 +530,7 @@ impl State { /// # Returns /// /// Tuple of (Block, post-State, collected attestations, signatures) + #[cfg(feature = "devnet1")] pub fn build_block( &self, slot: Slot, @@ -537,10 +539,18 @@ impl State { initial_attestations: Option>, available_signed_attestations: Option<&[SignedBlockWithAttestation]>, known_block_roots: Option<&std::collections::HashSet>, - ) -> Result<(Block, Self, Vec, BlockSignatures), String> { + ) -> Result< + ( + Block, + Self, + Vec, + PersistentList, + ), + String, + > { // Initialize empty attestation set for iterative collection let mut attestations = initial_attestations.unwrap_or_default(); - let mut signatures = BlockSignatures::default(); + let mut signatures = PersistentList::default(); // Advance state to target slot // Note: parent_root comes from fork choice and is already validated. @@ -649,6 +659,19 @@ impl State { } } } + + #[cfg(feature = "devnet2")] + pub fn build_block( + &self, + _slot: Slot, + _proposer_index: ValidatorIndex, + _parent_root: Bytes32, + _initial_attestations: Option>, + _available_signed_attestations: Option<&[SignedBlockWithAttestation]>, + _known_block_roots: Option<&std::collections::HashSet>, + ) -> Result<(Block, Self, Vec, BlockSignatures), String> { + Err("build_block is not implemented for devnet2".to_string()) + } } #[cfg(test)] @@ -705,6 +728,7 @@ mod tests { } #[test] + #[cfg(feature = "devnet1")] fn test_build_block() { // Create genesis state with validators let genesis_state = State::generate_genesis(Uint64(0), Uint64(4)); @@ -827,7 +851,7 @@ mod tests { block: block.clone(), proposer_attestation: Attestation::default(), }, - signature: BlockSignatures::default(), + signature: PersistentList::default(), }, true, // signatures are considered valid (not validating, just marking as valid) true, diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index d5e70f6..8ff5eed 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; -use containers::ssz::SszHash; +use containers::ssz::{PersistentList, SszHash}; use containers::{ - attestation::{Attestation, AttestationData, BlockSignatures}, + attestation::{Attestation, AttestationData}, block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, checkpoint::Checkpoint, config::Config, @@ -208,7 +208,7 @@ async fn main() { block: genesis_block, proposer_attestation: genesis_proposer_attestation, }, - signature: BlockSignatures::default(), + signature: PersistentList::default(), }; let config = Config { genesis_time }; From 0882afa2f722ac146df2c9d664b269a276432d69 Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Fri, 19 Dec 2025 11:11:48 +0200 Subject: [PATCH 04/48] minor cleanups --- lean_client/containers/src/block.rs | 7 ++++--- lean_client/containers/src/state.rs | 8 ++++++-- lean_client/containers/tests/test_vectors/runner.rs | 6 +++--- lean_client/containers/tests/unit_tests/common.rs | 10 +++++----- .../containers/tests/unit_tests/state_transition.rs | 6 ++++-- .../fork_choice/tests/fork_choice_test_vectors.rs | 8 ++++---- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index 832d2d8..e38b405 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -170,13 +170,14 @@ impl SignedBlockWithAttestation { // The ordering must be preserved: // 1. Block body attestations, // 2. The proposer attestation. - assert!( - signatures_vec.len() == all_attestations.len(), + assert_eq!( + signatures_vec.len(), + all_attestations.len(), "Number of signatures does not match number of attestations" ); let validators = &parent_state.validators; - let num_validators: u64 = validators.len_u64(); + let num_validators = validators.len_u64(); // Verify each attestation signature for (attestation, signature) in all_attestations.iter().zip(signatures_vec.iter()) { diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index bd36e33..0bee3c1 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -139,7 +139,11 @@ impl State { /// Simple RR proposer rule (round-robin). pub fn is_proposer(&self, index: ValidatorIndex) -> bool { - let num_validators: u64 = self.validators.len_u64(); + let num_validators = self.validators.len_u64(); + + if num_validators == 0 { + return false; // No validators + } (self.slot.0 % num_validators) == (index.0 % num_validators) } @@ -463,7 +467,7 @@ impl State { if validator_id < votes.len() && !votes[validator_id] { votes[validator_id] = true; - let num_validators: u64 = self.validators.len_u64(); + let num_validators = self.validators.len_u64(); let count = votes.iter().filter(|&&v| v).count(); if 3 * count >= 2 * num_validators as usize { diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index e2b829e..b069b8c 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -89,7 +89,7 @@ impl TestRunner { // Only check validator count if specified in post-state if let Some(expected_count) = post.validator_count { - let mut num_validators: u64 = state.validators.len_u64(); + let num_validators = state.validators.len_u64(); if num_validators as usize != expected_count { return Err(format!( @@ -450,7 +450,7 @@ impl TestRunner { let state = &test_case.pre; - let num_validators: u64 = state.validators.len_u64(); + let num_validators = state.validators.len_u64(); println!( " Genesis time: {}, slot: {}, validators: {}", state.config.genesis_time, state.slot.0, num_validators @@ -575,7 +575,7 @@ impl TestRunner { // Verify validator count if specified if let Some(expected_count) = post.validator_count { - let num_validators: u64 = state.validators.len_u64(); + let num_validators = state.validators.len_u64(); if num_validators as usize != expected_count { return Err(format!( diff --git a/lean_client/containers/tests/unit_tests/common.rs b/lean_client/containers/tests/unit_tests/common.rs index 1535732..841b959 100644 --- a/lean_client/containers/tests/unit_tests/common.rs +++ b/lean_client/containers/tests/unit_tests/common.rs @@ -4,10 +4,10 @@ use containers::{ slot::Slot, state::State, types::{Bytes32, ValidatorIndex}, - Attestation, Attestations, BlockSignatures, BlockWithAttestation, Config, - SignedBlockWithAttestation, Validators, + Attestation, Attestations, BlockWithAttestation, Config, SignedBlockWithAttestation, + Validators, }; -use ssz::PersistentList as List; +use ssz::PersistentList; pub const DEVNET_CONFIG_VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 pub const TEST_VALIDATOR_COUNT: usize = 4; // Actual validator count used in tests @@ -22,7 +22,7 @@ pub fn create_block( attestations: Option, ) -> SignedBlockWithAttestation { let body = BlockBody { - attestations: attestations.unwrap_or_else(List::default), + attestations: attestations.unwrap_or_else(PersistentList::default), }; let block_message = Block { @@ -38,7 +38,7 @@ pub fn create_block( block: block_message, proposer_attestation: Attestation::default(), }, - signature: BlockSignatures::default(), + signature: PersistentList::default(), } } diff --git a/lean_client/containers/tests/unit_tests/state_transition.rs b/lean_client/containers/tests/unit_tests/state_transition.rs index e530dde..6354bef 100644 --- a/lean_client/containers/tests/unit_tests/state_transition.rs +++ b/lean_client/containers/tests/unit_tests/state_transition.rs @@ -3,10 +3,11 @@ use containers::{ block::{hash_tree_root, Block, BlockWithAttestation, SignedBlockWithAttestation}, state::State, types::{Bytes32, Uint64}, - Attestation, BlockSignatures, Slot, + Attestation, Slot, }; use pretty_assertions::assert_eq; use rstest::fixture; +use ssz::PersistentList; #[path = "common.rs"] mod common; @@ -82,6 +83,7 @@ fn test_state_transition_invalid_signatures() { assert_eq!(result.unwrap_err(), "Block signatures must be valid"); } +#[cfg(feature = "devnet1")] #[test] fn test_state_transition_bad_state_root() { let state = genesis_state(); @@ -98,7 +100,7 @@ fn test_state_transition_bad_state_root() { block, proposer_attestation: Attestation::default(), }, - signature: BlockSignatures::default(), + signature: PersistentList::default(), }; let result = state.state_transition(final_signed_block_with_attestation, true); diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index d35c0bf..0437d9c 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -4,7 +4,7 @@ use fork_choice::{ }; use containers::{ - attestation::{Attestation, AttestationData, BlockSignatures, Signature, SignedAttestation}, + attestation::{Attestation, AttestationData, Signature, SignedAttestation}, block::{ hash_tree_root, Block, BlockBody, BlockHeader, BlockWithAttestation, SignedBlockWithAttestation, @@ -16,7 +16,7 @@ use containers::{ }; use serde::Deserialize; -use ssz::SszHash; +use ssz::{PersistentList, SszHash}; use std::collections::HashMap; use std::panic::AssertUnwindSafe; @@ -302,7 +302,7 @@ fn convert_test_anchor_block(test_block: &TestAnchorBlock) -> SignedBlockWithAtt block, proposer_attestation, }, - signature: BlockSignatures::default(), + signature: PersistentList::default(), } } @@ -334,7 +334,7 @@ fn convert_test_block( block, proposer_attestation, }, - signature: BlockSignatures::default(), + signature: PersistentList::default(), } } From 7fd8173f38a2f8b0e8fce088e8ed4b3ed9543d5e Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Tue, 23 Dec 2025 16:27:26 +0200 Subject: [PATCH 05/48] added tests, fixed some types --- lean_client/containers/src/attestation.rs | 104 ++++++++- lean_client/containers/src/block.rs | 204 ++++++++++++------ lean_client/containers/src/serde_helpers.rs | 5 +- lean_client/containers/src/state.rs | 19 +- .../unit_tests/attestation_aggregation.rs | 132 ++++++++++++ .../containers/tests/unit_tests/common.rs | 56 ++++- .../containers/tests/unit_tests/mod.rs | 1 + .../tests/unit_tests/state_process.rs | 1 + .../tests/unit_tests/state_transition.rs | 84 +++++++- 9 files changed, 529 insertions(+), 77 deletions(-) create mode 100644 lean_client/containers/tests/unit_tests/attestation_aggregation.rs diff --git a/lean_client/containers/src/attestation.rs b/lean_client/containers/src/attestation.rs index e96a595..302ef08 100644 --- a/lean_client/containers/src/attestation.rs +++ b/lean_client/containers/src/attestation.rs @@ -1,5 +1,6 @@ use crate::{Checkpoint, Slot, Uint64}; use serde::{Deserialize, Serialize}; +use ssz::BitList; use ssz::ByteVector; use ssz_derive::Ssz; use typenum::{Prod, Sum, U100, U12, U31}; @@ -21,11 +22,64 @@ pub type Attestations = ssz::PersistentList; pub type AggregatedAttestations = ssz::PersistentList; +#[cfg(feature = "devnet1")] pub type AttestationSignatures = ssz::PersistentList; +#[cfg(feature = "devnet2")] +pub type AttestationSignatures = ssz::PersistentList; + +#[cfg(feature = "devnet2")] +pub type NaiveAggregatedSignature = ssz::PersistentList; + /// Bitlist representing validator participation in an attestation. /// Limit is VALIDATOR_REGISTRY_LIMIT (4096). -pub type AggregationBits = ssz::BitList; +#[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] +pub struct AggregationBits(pub BitList); + +impl AggregationBits { + pub const LIMIT: u64 = 4096; + + pub fn from_validator_indices(indices: &[u64]) -> Self { + assert!( + !indices.is_empty(), + "Aggregated attestation must reference at least one validator" + ); + + let max_id = *indices.iter().max().unwrap(); + assert!( + max_id < Self::LIMIT, + "Validator index out of range for aggregation bits" + ); + + let mut bits = BitList::::with_length((max_id + 1) as usize); + + for i in 0..=max_id { + bits.set(i as usize, false); + } + + for &i in indices { + bits.set(i as usize, true); + } + + AggregationBits(bits) + } + + pub fn to_validator_indices(&self) -> Vec { + let indices: Vec = self + .0 + .iter() + .enumerate() + .filter_map(|(i, bit)| if *bit { Some(i as u64) } else { None }) + .collect(); + + assert!( + !indices.is_empty(), + "Aggregated attestation must reference at least one validator" + ); + + indices + } +} /// Naive list of validator signatures used for aggregation placeholders. /// Limit is VALIDATOR_REGISTRY_LIMIT (4096). @@ -57,13 +111,8 @@ pub struct Attestation { /// Validator attestation bundled with its signature. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct SignedAttestation { - #[cfg(feature = "devnet2")] - pub validator_id: u64, - #[cfg(feature = "devnet2")] - pub message: AttestationData, - #[cfg(feature = "devnet1")] pub message: Attestation, - /// signature over attestaion message only as it would be aggregated later in attestation + /// Signature aggregation produced by the leanVM (SNARKs in the future). pub signature: Signature, } @@ -73,12 +122,51 @@ pub struct AggregatedAttestation { /// Bitfield indicating which validators participated in the aggregation. pub aggregation_bits: AggregationBits, /// Combined attestation data similar to the beacon chain format. - /// + /// /// Multiple validator attestations are aggregated here without the complexity of /// committee assignments. pub data: AttestationData, } +impl AggregatedAttestation { + pub fn aggregate_by_data(attestations: &[Attestation]) -> Vec { + let mut groups: Vec<(AttestationData, Vec)> = Vec::new(); + + for attestation in attestations { + // Try to find an existing group with the same data + if let Some((_, validator_ids)) = groups + .iter_mut() + .find(|(data, _)| *data == attestation.data) + { + validator_ids.push(attestation.validator_id.0); + } else { + // Create a new group + groups.push((attestation.data.clone(), vec![attestation.validator_id.0])); + } + } + + groups + .into_iter() + .map(|(data, validator_ids)| AggregatedAttestation { + aggregation_bits: AggregationBits::from_validator_indices(&validator_ids), + data, + }) + .collect() + } + + pub fn to_plain(&self) -> Vec { + let validator_indices = self.aggregation_bits.to_validator_indices(); + + validator_indices + .into_iter() + .map(|validator_id| Attestation { + validator_id: Uint64(validator_id), + data: self.data.clone(), + }) + .collect() + } +} + /// Aggregated attestation bundled with aggregated signatures. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct SignedAggregatedAttestation { diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index e38b405..0acf1b2 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -4,9 +4,10 @@ use ssz_derive::Ssz; #[cfg(feature = "xmss-verify")] use leansig::signature::generalized_xmss::instantiations_poseidon::lifetime_2_to_the_20::target_sum::SIGTargetSumLifetime20W2NoOff; -use ssz::PersistentList; +use ssz::{PersistentList, SszHash}; use typenum::U4096; -use crate::attestation::AttestationSignatures; +use crate::attestation::{AggregatedAttestations, AttestationSignatures}; +use crate::validator::BlsPublicKey; /// The body of a block, containing payload data. /// @@ -15,7 +16,7 @@ use crate::attestation::AttestationSignatures; #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct BlockBody { #[cfg(feature = "devnet2")] - pub attestations: VariableList, + pub attestations: AggregatedAttestations, #[cfg(feature = "devnet1")] #[serde(with = "crate::serde_helpers")] pub attestations: Attestations, @@ -51,7 +52,7 @@ pub struct BlockWithAttestation { pub proposer_attestation: Attestation, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Ssz, Deserialize, Default)] pub struct BlockSignatures { pub attestation_signatures: AttestationSignatures, pub proposer_signature: Signature, @@ -127,6 +128,7 @@ impl SignedBlockWithAttestation { /// /// - Spec: /// - XMSS Library: + #[cfg(feature = "devnet1")] pub fn verify_signatures(&self, parent_state: State) -> bool { // Unpack the signed block components let block = &self.message.block; @@ -198,68 +200,148 @@ impl SignedBlockWithAttestation { // - The attestation has not been tampered with // - The signature was created at the correct epoch (slot) - #[cfg(feature = "xmss-verify")] - { - use leansig::serialization::Serializable; - use leansig::signature::SignatureScheme; - - // Compute the message hash from the attestation - let message_bytes: [u8; 32] = hash_tree_root(attestation).0.into(); - let epoch = attestation.data.slot.0 as u32; - - // Get public key bytes - use as_bytes() method - let pubkey_bytes = validator.pubkey.0.as_bytes(); - - // Deserialize the public key using Serializable trait - type PubKey = ::PublicKey; - let pubkey = match PubKey::from_bytes(pubkey_bytes) { - Ok(pk) => pk, - Err(e) => { - eprintln!( - "Failed to deserialize public key at slot {:?}: {:?}", - attestation.data.slot, e - ); - return false; - } - }; - - // Get signature bytes - use as_bytes() method - let sig_bytes = signature.as_bytes(); - - // Deserialize the signature using Serializable trait - type Sig = ::Signature; - let sig = match Sig::from_bytes(sig_bytes) { - Ok(s) => s, - Err(e) => { - eprintln!( - "Failed to deserialize signature at slot {:?}: {:?}", - attestation.data.slot, e - ); - return false; - } - }; - - // Verify the signature - if !SIGTargetSumLifetime20W2NoOff::verify(&pubkey, epoch, &message_bytes, &sig) { - eprintln!( - "XMSS signature verification failed at slot {:?}", - attestation.data.slot - ); - return false; - } - } + let message_bytes: [u8; 32] = hash_tree_root(attestation).0.into(); + + assert!( + verify_xmss_signature( + validator.pubkey.0.as_bytes(), + attestation.data.slot, + &message_bytes, + &signature, + ), + "Attestation signature verification failed" + ); + } + + true + } + + #[cfg(feature = "devnet2")] + pub fn verify_signatures(&self, parent_state: State) -> bool { + // Unpack the signed block components + let block = &self.message.block; + let signatures = &self.signature; + let aggregated_attestations = block.body.attestations.clone(); + let attestation_signatures = signatures.attestation_signatures.clone(); + + // Verify signature count matches aggregated attestation count + assert_eq!( + aggregated_attestations.len_u64(), + attestation_signatures.len_u64(), + "Number of signatures does not match number of attestations" + ); + + let validators = &parent_state.validators; + let num_validators = validators.len_u64(); + + // Verify each attestation signature + for (aggregated_attestation, aggregated_signature) in (&aggregated_attestations) + .into_iter() + .zip((&attestation_signatures).into_iter()) + { + let validator_ids = aggregated_attestation + .aggregation_bits + .to_validator_indices(); + + assert_eq!( + aggregated_signature.len_u64(), + validator_ids.len() as u64, + "Aggregated attestation signature count mismatch" + ); - #[cfg(not(feature = "xmss-verify"))] + let attestation_root = aggregated_attestation.data.hash_tree_root(); + + // Loop through zipped validator IDs and their corresponding signatures + // Verify each individual signature within the aggregated attestation + for (validator_id, signature) in + validator_ids.iter().zip(aggregated_signature.into_iter()) { - // Placeholder: XMSS verification disabled - // To enable, compile with --features xmss-verify - let _pubkey = &validator.pubkey; - let _slot = attestation.data.slot; - let _message = hash_tree_root(attestation); - let _sig = signature; + // Ensure validator exists in the active set + assert!( + *validator_id < num_validators, + "Validator index out of range" + ); + + let validator = validators.get(*validator_id).expect("validator must exist"); + + // Get the actual payload root for the attestation data + let attestation_root: [u8; 32] = + hash_tree_root(&aggregated_attestation.data).0.into(); + + // Verify the XMSS signature + assert!( + verify_xmss_signature( + validator.pubkey.0.as_bytes(), + aggregated_attestation.data.slot, + &attestation_root, + signature, + ), + "Attestation signature verification failed" + ); } + + // Verify the proposer attestation signature + let proposer_attestation = self.message.proposer_attestation.clone(); + let proposer_signature = signatures.proposer_signature; + + assert!( + proposer_attestation.validator_id.0 < num_validators, + "Proposer index out of range" + ); + + let proposer = validators + .get(proposer_attestation.validator_id.0) + .expect("proposer must exist"); + + let proposer_root: [u8; 32] = hash_tree_root(&proposer_attestation).0.into(); + assert!( + verify_xmss_signature( + proposer.pubkey.0.as_bytes(), + proposer_attestation.data.slot, + &proposer_root, + &proposer_signature, + ), + "Proposer attestation signature verification failed" + ); } true } } + +#[cfg(feature = "xmss-verify")] +pub fn verify_xmss_signature( + pubkey_bytes: &[u8], + slot: Slot, + message_bytes: &[u8; 32], + signature: &Signature, +) -> bool { + use leansig::serialization::Serializable; + use leansig::signature::SignatureScheme; + + let epoch = slot.0 as u32; + + type PubKey = ::PublicKey; + let pubkey = match PubKey::from_bytes(pubkey_bytes) { + Ok(pk) => pk, + Err(_) => return false, + }; + + type Sig = ::Signature; + let sig = match Sig::from_bytes(signature.as_bytes()) { + Ok(s) => s, + Err(_) => return false, + }; + + SIGTargetSumLifetime20W2NoOff::verify(&pubkey, epoch, message_bytes, &sig) +} + +#[cfg(not(feature = "xmss-verify"))] +pub fn verify_xmss_signature( + _pubkey_bytes: &[u8], + _slot: Slot, + _message_bytes: &[u8; 32], + _signature: &Signature, +) -> bool { + true +} diff --git a/lean_client/containers/src/serde_helpers.rs b/lean_client/containers/src/serde_helpers.rs index 0568f71..01604e5 100644 --- a/lean_client/containers/src/serde_helpers.rs +++ b/lean_client/containers/src/serde_helpers.rs @@ -187,6 +187,7 @@ pub mod signature { /// where each signature can be either hex string or structured XMSS format pub mod block_signatures { use super::*; + use crate::block::BlockSignatures; use crate::Signature; use serde_json::Value; use ssz::PersistentList; @@ -309,11 +310,11 @@ pub mod block_signatures { } #[cfg(feature = "devnet2")] - pub fn serialize(value: &BlockSignatures, serializer: S) -> Result + pub fn serialize(_value: &BlockSignatures, _serializer: S) -> Result where S: Serializer, { - Err(serde::de::Error::custom( + Err(serde::ser::Error::custom( "BlockSignatures serialization not implemented for devnet2", )) } diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index 0bee3c1..6318cdc 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -12,6 +12,8 @@ use ssz::{PersistentList as List, PersistentList}; use ssz_derive::Ssz; use std::collections::BTreeMap; use typenum::U4096; +use crate::attestation::AggregatedAttestations; +use crate::block::BlockSignatures; pub const VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 pub const JUSTIFICATION_ROOTS_LIMIT: usize = 1 << 18; // 262144 @@ -286,7 +288,20 @@ impl State { pub fn process_block(&self, block: &Block) -> Result { let state = self.process_block_header(block)?; + #[cfg(feature = "devnet1")] let state_after_ops = state.process_attestations(&block.body.attestations); + #[cfg(feature = "devnet2")] + let state_after_ops = { + let mut unaggregated_attestations = Attestations::default(); + for aggregated_attestation in &block.body.attestations { + let plain_attestations = aggregated_attestation.to_plain(); + // For each attestatio in the vector, push to the list + for attestation in plain_attestations { + unaggregated_attestations.push(attestation).map_err(|e| format!("Failed to push attestation: {:?}", e))?; + } + } + state.process_attestations(&unaggregated_attestations) + }; // State root validation is handled by state_transition_with_validation when needed @@ -688,7 +703,7 @@ mod tests { config: st.config.clone(), ..st.clone() } - .is_proposer(ValidatorIndex(0))); + .is_proposer(ValidatorIndex(0))); } #[test] @@ -828,6 +843,7 @@ mod tests { } #[test] + #[cfg(feature = "devnet1")] fn test_build_block_advances_state() { // Create genesis state let genesis_state = State::generate_genesis(Uint64(0), Uint64(10)); @@ -868,6 +884,7 @@ mod tests { } #[test] + #[cfg(feature = "devnet1")] fn test_build_block_state_root_matches() { // Create genesis state let genesis_state = State::generate_genesis(Uint64(0), Uint64(3)); diff --git a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs new file mode 100644 index 0000000..285aa46 --- /dev/null +++ b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs @@ -0,0 +1,132 @@ +#[cfg(feature = "devnet2")] +#[cfg(test)] +mod tests { + use containers::attestation::{AggregatedAttestation, AggregationBits, Attestation, AttestationData}; + use containers::{Bytes32, Uint64}; + use containers::checkpoint::Checkpoint; + use containers::slot::Slot; + + #[test] + fn test_aggregated_attestation_structure() { + let att_data = AttestationData { + slot: Slot(5), + head: Checkpoint { + root: Bytes32::default(), + slot: Slot(4), + }, + target: Checkpoint { + root: Bytes32::default(), + slot: Slot(3), + }, + source: Checkpoint { + root: Bytes32::default(), + slot: Slot(2), + } + }; + + let bits = AggregationBits::from_validator_indices(&vec![2, 7]); + let agg = AggregatedAttestation { + aggregation_bits: bits.clone(), + data: att_data.clone() + }; + + let indices = agg.aggregation_bits.to_validator_indices(); + assert_eq!(indices.into_iter().collect::>(), vec![2, 7].into_iter().collect()); + assert_eq!(agg.data, att_data); + } + + #[test] + fn test_aggregate_attestations_by_common_data() { + let att_data1 = AttestationData { + slot: Slot(5), + head: Checkpoint { + root: Bytes32::default(), + slot: Slot(4), + }, + target: Checkpoint { + root: Bytes32::default(), + slot: Slot(3), + }, + source: Checkpoint { + root: Bytes32::default(), + slot: Slot(2), + } + }; + let att_data2 = AttestationData { + slot: Slot(6), + head: Checkpoint { + root: Bytes32::default(), + slot: Slot(5), + }, + target: Checkpoint { + root: Bytes32::default(), + slot: Slot(4), + }, + source: Checkpoint { + root: Bytes32::default(), + slot: Slot(3), + } + }; + + let attestations = vec![ + Attestation { + validator_id: Uint64(1), + data: att_data1.clone(), + }, + Attestation { + validator_id: Uint64(3), + data: att_data1.clone(), + }, + Attestation { + validator_id: Uint64(5), + data: att_data2.clone(), + }, + ]; + + let aggregated = AggregatedAttestation::aggregate_by_data(&attestations); + assert_eq!(aggregated.len(), 2); + + let agg1 = aggregated.iter().find(|agg| agg.data == att_data1).unwrap(); + let validator_ids1 = agg1.aggregation_bits.to_validator_indices(); + assert_eq!(validator_ids1.into_iter().collect::>(), vec![1, 3].into_iter().collect()); + + let agg2 = aggregated.iter().find(|agg| agg.data == att_data2).unwrap(); + let validator_ids2 = agg2.aggregation_bits.to_validator_indices(); + assert_eq!(validator_ids2, vec![5]); + } + + #[test] + fn test_aggregate_empty_attestations() { + let aggregated = AggregatedAttestation::aggregate_by_data(&[]); + assert!(aggregated.is_empty()); + } + + #[test] + fn test_aggregate_single_attestation() { + let att_data = AttestationData { + slot: Slot(5), + head: Checkpoint { + root: Bytes32::default(), + slot: Slot(4), + }, + target: Checkpoint { + root: Bytes32::default(), + slot: Slot(3), + }, + source: Checkpoint { + root: Bytes32::default(), + slot: Slot(2), + } + }; + + let attestations = vec![Attestation { + validator_id: Uint64(5), + data: att_data.clone(), + }]; + let aggregated = AggregatedAttestation::aggregate_by_data(&attestations); + + assert_eq!(aggregated.len(), 1); + let validator_ids = aggregated[0].aggregation_bits.to_validator_indices(); + assert_eq!(validator_ids, vec![5]); + } +} diff --git a/lean_client/containers/tests/unit_tests/common.rs b/lean_client/containers/tests/unit_tests/common.rs index 841b959..1a648b8 100644 --- a/lean_client/containers/tests/unit_tests/common.rs +++ b/lean_client/containers/tests/unit_tests/common.rs @@ -1,13 +1,15 @@ +use containers::block::BlockSignatures; use containers::{ block::{hash_tree_root, Block, BlockBody, BlockHeader}, checkpoint::Checkpoint, slot::Slot, state::State, types::{Bytes32, ValidatorIndex}, - Attestation, Attestations, BlockWithAttestation, Config, SignedBlockWithAttestation, - Validators, + AggregatedAttestation, Attestation, Attestations, BlockWithAttestation, Config, Signature, + SignedBlockWithAttestation, Validators, }; use ssz::PersistentList; +use typenum::U4096; pub const DEVNET_CONFIG_VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 pub const TEST_VALIDATOR_COUNT: usize = 4; // Actual validator count used in tests @@ -21,9 +23,40 @@ pub fn create_block( parent_header: &mut BlockHeader, attestations: Option, ) -> SignedBlockWithAttestation { + #[cfg(feature = "devnet1")] let body = BlockBody { attestations: attestations.unwrap_or_else(PersistentList::default), }; + #[cfg(feature = "devnet2")] + let body = BlockBody { + attestations: { + let attestations_vec = attestations.unwrap_or_default(); + + // Convert PersistentList into a Vec + let attestations_vec: Vec = + attestations_vec.into_iter().cloned().collect(); + + let aggregated: Vec = + AggregatedAttestation::aggregate_by_data(&attestations_vec); + + let aggregated: Vec = + AggregatedAttestation::aggregate_by_data(&attestations_vec); + + // Create a new empty PersistentList + let mut persistent_list: PersistentList = + PersistentList::default(); + + // Push each aggregated attestation + for agg in aggregated { + persistent_list + .push(agg) + .expect("PersistentList capacity exceeded"); + } + + persistent_list + }, + // other BlockBody fields... + }; let block_message = Block { slot: Slot(slot), @@ -33,13 +66,28 @@ pub fn create_block( body: body, }; - SignedBlockWithAttestation { + #[cfg(feature = "devnet1")] + let return_value = SignedBlockWithAttestation { message: BlockWithAttestation { block: block_message, proposer_attestation: Attestation::default(), }, signature: PersistentList::default(), - } + }; + + #[cfg(feature = "devnet2")] + let return_value = SignedBlockWithAttestation { + message: BlockWithAttestation { + block: block_message, + proposer_attestation: Attestation::default(), + }, + signature: BlockSignatures { + attestation_signatures: PersistentList::default(), + proposer_signature: Signature::default(), + }, + }; + + return_value } pub fn create_attestations(indices: &[usize]) -> Vec { diff --git a/lean_client/containers/tests/unit_tests/mod.rs b/lean_client/containers/tests/unit_tests/mod.rs index 16a5646..b9f442f 100644 --- a/lean_client/containers/tests/unit_tests/mod.rs +++ b/lean_client/containers/tests/unit_tests/mod.rs @@ -4,3 +4,4 @@ mod state_basic; mod state_justifications; mod state_process; mod state_transition; +mod attestation_aggregation; diff --git a/lean_client/containers/tests/unit_tests/state_process.rs b/lean_client/containers/tests/unit_tests/state_process.rs index f423818..5df98cf 100644 --- a/lean_client/containers/tests/unit_tests/state_process.rs +++ b/lean_client/containers/tests/unit_tests/state_process.rs @@ -130,6 +130,7 @@ fn test_process_block_header_invalid( } // This test verifies that attestations correctly justify and finalize slots +#[cfg(feature = "devnet1")] #[test] fn test_process_attestations_justification_and_finalization() { let mut state = genesis_state(); diff --git a/lean_client/containers/tests/unit_tests/state_transition.rs b/lean_client/containers/tests/unit_tests/state_transition.rs index 6354bef..7725210 100644 --- a/lean_client/containers/tests/unit_tests/state_transition.rs +++ b/lean_client/containers/tests/unit_tests/state_transition.rs @@ -3,7 +3,7 @@ use containers::{ block::{hash_tree_root, Block, BlockWithAttestation, SignedBlockWithAttestation}, state::State, types::{Bytes32, Uint64}, - Attestation, Slot, + Attestation, Attestations, Slot, }; use pretty_assertions::assert_eq; use rstest::fixture; @@ -30,8 +30,23 @@ fn test_state_transition_full() { // Use process_block_header + process_operations to avoid state root validation during setup let state_after_header = state_at_slot_1.process_block_header(&block).unwrap(); + + #[cfg(feature = "devnet1")] let expected_state = state_after_header.process_attestations(&block.body.attestations); + #[cfg(feature = "devnet2")] + let expected_state = { + let mut unaggregated_attestations = Attestations::default(); + for aggregated_attestation in &block.body.attestations { + let plain_attestations = aggregated_attestation.to_plain(); + // For each attestatio in the vector, push to the list + for attestation in plain_attestations { + unaggregated_attestations.push(attestation); + } + } + state_after_header.process_attestations(&unaggregated_attestations) + }; + let block_with_correct_root = Block { state_root: hash_tree_root(&expected_state), ..block @@ -63,8 +78,23 @@ fn test_state_transition_invalid_signatures() { // Use process_block_header + process_operations to avoid state root validation during setup let state_after_header = state_at_slot_1.process_block_header(&block).unwrap(); + + #[cfg(feature = "devnet1")] let expected_state = state_after_header.process_attestations(&block.body.attestations); + #[cfg(feature = "devnet2")] + let expected_state = { + let mut list = Attestations::default(); + for aggregated_attestation in &block.body.attestations { + let plain_attestations = aggregated_attestation.to_plain(); + // For each attestatio in the vector, push to the list + for attestation in plain_attestations { + list.push(attestation); + } + } + list + }; + let block_with_correct_root = Block { state_root: hash_tree_root(&expected_state), ..block @@ -107,3 +137,55 @@ fn test_state_transition_bad_state_root() { assert!(result.is_err()); assert_eq!(result.unwrap_err(), "Invalid block state root"); } + +#[cfg(feature = "devnet2")] +#[test] +fn test_state_transition_devnet2() { + let state = genesis_state(); + let mut state_at_slot_1 = state.process_slots(Slot(1)).unwrap(); + + // Create a block with attestations for devnet2 + let signed_block_with_attestation = + create_block(1, &mut state_at_slot_1.latest_block_header, None); + let block = signed_block_with_attestation.message.block.clone(); + + // Process the block header and attestations + let state_after_header = state_at_slot_1.process_block_header(&block).unwrap(); + + #[cfg(feature = "devnet1")] + let expected_state = state_after_header.process_attestations(&block.body.attestations); + + #[cfg(feature = "devnet2")] + let expected_state = { + let mut unaggregated_attestations = Attestations::default(); + for aggregated_attestation in &block.body.attestations { + let plain_attestations = aggregated_attestation.to_plain(); + // For each attestatio in the vector, push to the list + for attestation in plain_attestations { + unaggregated_attestations.push(attestation); + } + } + state_after_header.process_attestations(&unaggregated_attestations) + }; + + // Ensure the state root matches the expected state + let block_with_correct_root = Block { + state_root: hash_tree_root(&expected_state), + ..block + }; + + let final_signed_block_with_attestation = SignedBlockWithAttestation { + message: BlockWithAttestation { + block: block_with_correct_root, + proposer_attestation: signed_block_with_attestation.message.proposer_attestation, + }, + signature: signed_block_with_attestation.signature, + }; + + // Perform the state transition and validate the result + let final_state = state + .state_transition(final_signed_block_with_attestation, true) + .unwrap(); + + assert_eq!(final_state, expected_state); +} From 52dfc5b5e8bcffe99970ad15f6fd8834aeb81196 Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Mon, 29 Dec 2025 12:37:57 +0200 Subject: [PATCH 06/48] fixed environment selection by adding a minimal crate `env-config`. Added readme on how to select devnet --- lean_client/Cargo.lock | 8 + lean_client/Cargo.toml | 8 +- lean_client/ENVIRONMENT_SELECTION.md | 26 +++ lean_client/containers/Cargo.toml | 11 +- lean_client/containers/src/attestation.rs | 6 +- lean_client/containers/src/state.rs | 16 +- lean_client/containers/tests/main.rs | 2 +- .../tests/test_vectors/block_processing.rs | 10 ++ .../containers/tests/test_vectors/runner.rs | 2 +- .../tests/test_vectors/verify_signatures.rs | 6 +- lean_client/env-config/Cargo.toml | 12 ++ lean_client/env-config/src/lib.rs | 1 + lean_client/fork_choice/Cargo.toml | 8 +- lean_client/fork_choice/src/handlers.rs | 149 ++++++++++++++---- lean_client/fork_choice/src/store.rs | 3 + .../tests/fork_choice_test_vectors.rs | 10 ++ .../fork_choice/tests/unit_tests/votes.rs | 9 ++ lean_client/networking/Cargo.toml | 6 + lean_client/networking/src/network/service.rs | 7 + lean_client/networking/src/types.rs | 11 ++ lean_client/src/main.rs | 38 ++++- lean_client/validator/Cargo.toml | 3 + lean_client/validator/src/lib.rs | 94 ++++++++++- 23 files changed, 391 insertions(+), 55 deletions(-) create mode 100644 lean_client/ENVIRONMENT_SELECTION.md create mode 100644 lean_client/env-config/Cargo.toml create mode 100644 lean_client/env-config/src/lib.rs diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index 5b622b9..5d46d69 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -847,6 +847,7 @@ dependencies = [ name = "containers" version = "0.1.0" dependencies = [ + "env-config", "hex", "leansig", "pretty_assertions", @@ -1376,6 +1377,10 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "env-config" +version = "0.1.0" + [[package]] name = "equivalent" version = "1.0.2" @@ -1553,6 +1558,7 @@ name = "fork-choice" version = "0.1.0" dependencies = [ "containers", + "env-config", "serde", "serde_json", "ssz", @@ -3168,6 +3174,7 @@ dependencies = [ "containers", "derive_more", "discv5", + "env-config", "futures", "hex", "libp2p", @@ -5253,6 +5260,7 @@ name = "validator" version = "0.1.0" dependencies = [ "containers", + "env-config", "fork-choice", "leansig", "serde", diff --git a/lean_client/Cargo.toml b/lean_client/Cargo.toml index 7d98ae7..9e72c43 100644 --- a/lean_client/Cargo.toml +++ b/lean_client/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["chain", "containers", "fork_choice", "networking", "validator"] +members = ["chain", "containers", "env-config", "fork_choice", "networking", "validator"] resolver = "2" [workspace.package] @@ -14,7 +14,7 @@ containers = { path = "./containers" } fork_choice = { path = "./fork_choice" } networking = { path = "./networking" } validator = { path = "./validator" } -libp2p = {version = "0.56.0", default-features = false, features = [ +libp2p = { version = "0.56.0", default-features = false, features = [ 'dns', 'gossipsub', 'identify', @@ -52,8 +52,10 @@ version = "0.1.0" edition = "2021" [features] -default = ["xmss-signing"] +default = ["devnet2", "xmss-signing"] xmss-signing = ["validator/xmss-signing"] +devnet1 = ["containers/devnet1", "fork-choice/devnet1", "networking/devnet1", "validator/devnet1"] +devnet2 = ["containers/devnet2", "fork-choice/devnet2", "networking/devnet2", "validator/devnet2"] [dependencies] chain = { path = "./chain" } diff --git a/lean_client/ENVIRONMENT_SELECTION.md b/lean_client/ENVIRONMENT_SELECTION.md new file mode 100644 index 0000000..d906c9d --- /dev/null +++ b/lean_client/ENVIRONMENT_SELECTION.md @@ -0,0 +1,26 @@ +### To select which devnet you want to compile + +#### Option A +- Change the default features in root `Cargo.toml`: +```toml +[features] +default = ["devnet1", "<...other features>"] # Change to "devnet2" if needed +devnet1 = [...] +devnet2 = [...] +``` + +#### Option B +- Use the `--no-default-features` flag and specify the desired devnet feature when building or running the project: +```bash +cargo build --no-default-features --features devnet1 # Change to devnet2 +``` + + +### Running tests for a specific devnet + +From root directory, use the following command: +```bash +cargo test -p --no-default-features --features devnet1 # Change to devnet2 +``` + +Use `` to specify the crate you want to test. \ No newline at end of file diff --git a/lean_client/containers/Cargo.toml b/lean_client/containers/Cargo.toml index e5aa52f..29e8ecd 100644 --- a/lean_client/containers/Cargo.toml +++ b/lean_client/containers/Cargo.toml @@ -5,17 +5,18 @@ edition = "2021" [features] xmss-verify = ["leansig"] -default = ["devnet1"] -devnet1 = [] -devnet2 = [] +default = [] +devnet1 = ["env-config/devnet1"] +devnet2 = ["env-config/devnet2"] [lib] name = "containers" path = "src/lib.rs" [dependencies] -ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop" } -ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop" } +env-config = { path = "../env-config", default-features = false } +ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop", submodules = true } +ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop", submodules = false } typenum = "1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/lean_client/containers/src/attestation.rs b/lean_client/containers/src/attestation.rs index 302ef08..9c3537c 100644 --- a/lean_client/containers/src/attestation.rs +++ b/lean_client/containers/src/attestation.rs @@ -111,8 +111,12 @@ pub struct Attestation { /// Validator attestation bundled with its signature. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct SignedAttestation { + #[cfg(feature = "devnet2")] + pub validator_id: u64, + #[cfg(feature = "devnet2")] + pub message: AttestationData, + #[cfg(feature = "devnet1")] pub message: Attestation, - /// Signature aggregation produced by the leanVM (SNARKs in the future). pub signature: Signature, } diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index 6318cdc..fa13011 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -1,8 +1,10 @@ +use crate::attestation::AggregatedAttestations; +use crate::block::BlockSignatures; use crate::validator::Validator; use crate::{ block::{hash_tree_root, Block, BlockBody, BlockHeader, SignedBlockWithAttestation}, - Attestation, Attestations, Bytes32, Checkpoint, Config, Signature, Slot, Uint64, - ValidatorIndex, + Attestation, Attestations, Bytes32, Checkpoint, Config, Signature, SignedAttestation, Slot, + Uint64, ValidatorIndex, }; use crate::{ HistoricalBlockHashes, JustificationRoots, JustificationsValidators, JustifiedSlots, Validators, @@ -12,8 +14,6 @@ use ssz::{PersistentList as List, PersistentList}; use ssz_derive::Ssz; use std::collections::BTreeMap; use typenum::U4096; -use crate::attestation::AggregatedAttestations; -use crate::block::BlockSignatures; pub const VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 pub const JUSTIFICATION_ROOTS_LIMIT: usize = 1 << 18; // 262144 @@ -297,7 +297,9 @@ impl State { let plain_attestations = aggregated_attestation.to_plain(); // For each attestatio in the vector, push to the list for attestation in plain_attestations { - unaggregated_attestations.push(attestation).map_err(|e| format!("Failed to push attestation: {:?}", e))?; + unaggregated_attestations + .push(attestation) + .map_err(|e| format!("Failed to push attestation: {:?}", e))?; } } state.process_attestations(&unaggregated_attestations) @@ -686,7 +688,7 @@ impl State { _proposer_index: ValidatorIndex, _parent_root: Bytes32, _initial_attestations: Option>, - _available_signed_attestations: Option<&[SignedBlockWithAttestation]>, + _available_signed_attestations: Option<&[SignedAttestation]>, _known_block_roots: Option<&std::collections::HashSet>, ) -> Result<(Block, Self, Vec, BlockSignatures), String> { Err("build_block is not implemented for devnet2".to_string()) @@ -703,7 +705,7 @@ mod tests { config: st.config.clone(), ..st.clone() } - .is_proposer(ValidatorIndex(0))); + .is_proposer(ValidatorIndex(0))); } #[test] diff --git a/lean_client/containers/tests/main.rs b/lean_client/containers/tests/main.rs index ee67df1..f951ffe 100644 --- a/lean_client/containers/tests/main.rs +++ b/lean_client/containers/tests/main.rs @@ -1,4 +1,4 @@ -// tests/main.rs - Test entry point +// tests/lib - Test entry point mod debug_deserialize; mod test_vectors; mod unit_tests; diff --git a/lean_client/containers/tests/test_vectors/block_processing.rs b/lean_client/containers/tests/test_vectors/block_processing.rs index d2e1c9e..5bbc997 100644 --- a/lean_client/containers/tests/test_vectors/block_processing.rs +++ b/lean_client/containers/tests/test_vectors/block_processing.rs @@ -2,6 +2,7 @@ use super::runner::TestRunner; #[test] +#[cfg(feature = "devnet1")] fn test_process_first_block_after_genesis() { let test_path = "../tests/test_vectors/test_blocks/test_process_first_block_after_genesis.json"; TestRunner::run_block_processing_test(test_path) @@ -9,12 +10,14 @@ fn test_process_first_block_after_genesis() { } #[test] +#[cfg(feature = "devnet1")] fn test_blocks_with_gaps() { let test_path = "../tests/test_vectors/test_blocks/test_blocks_with_gaps.json"; TestRunner::run_block_processing_test(test_path).expect("test_blocks_with_gaps failed"); } #[test] +#[cfg(feature = "devnet1")] fn test_linear_chain_multiple_blocks() { let test_path = "../tests/test_vectors/test_blocks/test_linear_chain_multiple_blocks.json"; TestRunner::run_block_processing_test(test_path) @@ -22,18 +25,21 @@ fn test_linear_chain_multiple_blocks() { } #[test] +#[cfg(feature = "devnet1")] fn test_block_extends_deep_chain() { let test_path = "../tests/test_vectors/test_blocks/test_block_extends_deep_chain.json"; TestRunner::run_block_processing_test(test_path).expect("test_block_extends_deep_chain failed"); } #[test] +#[cfg(feature = "devnet1")] fn test_empty_blocks() { let test_path = "../tests/test_vectors/test_blocks/test_empty_blocks.json"; TestRunner::run_block_processing_test(test_path).expect("test_empty_blocks failed"); } #[test] +#[cfg(feature = "devnet1")] fn test_empty_blocks_with_missed_slots() { let test_path = "../tests/test_vectors/test_blocks/test_empty_blocks_with_missed_slots.json"; TestRunner::run_block_processing_test(test_path) @@ -41,6 +47,7 @@ fn test_empty_blocks_with_missed_slots() { } #[test] +#[cfg(feature = "devnet1")] fn test_block_at_large_slot_number() { let test_path = "../tests/test_vectors/test_blocks/test_block_at_large_slot_number.json"; TestRunner::run_block_processing_test(test_path) @@ -50,6 +57,7 @@ fn test_block_at_large_slot_number() { // Invalid block tests (expecting failures) #[test] +#[cfg(feature = "devnet1")] fn test_block_with_invalid_parent_root() { let test_path = "../tests/test_vectors/test_blocks/test_block_with_invalid_parent_root.json"; TestRunner::run_block_processing_test(test_path) @@ -57,6 +65,7 @@ fn test_block_with_invalid_parent_root() { } #[test] +#[cfg(feature = "devnet1")] fn test_block_with_invalid_proposer() { let test_path = "../tests/test_vectors/test_blocks/test_block_with_invalid_proposer.json"; TestRunner::run_block_processing_test(test_path) @@ -64,6 +73,7 @@ fn test_block_with_invalid_proposer() { } #[test] +#[cfg(feature = "devnet1")] fn test_block_with_invalid_state_root() { let test_path = "../tests/test_vectors/test_blocks/test_block_with_invalid_state_root.json"; TestRunner::run_block_processing_test(test_path) diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index b069b8c..f6942fe 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -598,6 +598,7 @@ impl TestRunner { /// Test runner for verify_signatures test vectors /// Tests XMSS signature verification on SignedBlockWithAttestation + #[cfg(feature = "devnet1")] pub fn run_verify_signatures_test>( path: P, ) -> Result<(), Box> { @@ -626,7 +627,6 @@ impl TestRunner { ); let attestation_count = signed_block.message.block.body.attestations.len_u64(); - println!(" Attestations in block: {}", attestation_count); println!( " Proposer attestation validator: {}", diff --git a/lean_client/containers/tests/test_vectors/verify_signatures.rs b/lean_client/containers/tests/test_vectors/verify_signatures.rs index cd813a9..13692f7 100644 --- a/lean_client/containers/tests/test_vectors/verify_signatures.rs +++ b/lean_client/containers/tests/test_vectors/verify_signatures.rs @@ -15,14 +15,14 @@ use super::runner::TestRunner; // Without xmss-verify feature, they pass because structural validation succeeds. #[test] -#[ignore = "TODO"] +#[cfg(feature = "devnet1")] fn test_proposer_signature() { let test_path = "../tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json"; TestRunner::run_verify_signatures_test(test_path).expect("test_proposer_signature failed"); } #[test] -#[ignore = "TODO"] +#[cfg(feature = "devnet1")] fn test_proposer_and_attester_signatures() { let test_path = "../tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json"; TestRunner::run_verify_signatures_test(test_path) @@ -35,6 +35,7 @@ fn test_proposer_and_attester_signatures() { // Run with `cargo test --features xmss-verify` to enable full signature verification. #[test] +#[cfg(feature = "devnet1")] #[ignore = "Requires xmss-verify feature for actual signature validation. Run with: cargo test --features xmss-verify"] fn test_invalid_signature() { let test_path = "../tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json"; @@ -42,6 +43,7 @@ fn test_invalid_signature() { } #[test] +#[cfg(feature = "devnet1")] #[ignore = "Requires xmss-verify feature for actual signature validation. Run with: cargo test --features xmss-verify"] fn test_mixed_valid_invalid_signatures() { let test_path = "../tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_mixed_valid_invalid_signatures.json"; diff --git a/lean_client/env-config/Cargo.toml b/lean_client/env-config/Cargo.toml new file mode 100644 index 0000000..4b761e5 --- /dev/null +++ b/lean_client/env-config/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "env-config" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true + +[features] +devnet1 = [] +devnet2 = [] + +[dependencies] diff --git a/lean_client/env-config/src/lib.rs b/lean_client/env-config/src/lib.rs new file mode 100644 index 0000000..972005d --- /dev/null +++ b/lean_client/env-config/src/lib.rs @@ -0,0 +1 @@ +// Empty on purpose \ No newline at end of file diff --git a/lean_client/fork_choice/Cargo.toml b/lean_client/fork_choice/Cargo.toml index f906f59..b16f561 100644 --- a/lean_client/fork_choice/Cargo.toml +++ b/lean_client/fork_choice/Cargo.toml @@ -3,8 +3,14 @@ name = "fork-choice" version = "0.1.0" edition = "2021" +[features] +default = [] +devnet1 = ["containers/devnet1", "env-config/devnet1"] +devnet2 = ["containers/devnet2", "env-config/devnet1"] + [dependencies] -containers = { path = "../containers" } +env-config = { path = "../env-config", default-features = false } +containers = { path = "../containers", default-features = false } ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop"} ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop" } typenum = "1.17.0" diff --git a/lean_client/fork_choice/src/handlers.rs b/lean_client/fork_choice/src/handlers.rs index 25eb50d..9f3837d 100644 --- a/lean_client/fork_choice/src/handlers.rs +++ b/lean_client/fork_choice/src/handlers.rs @@ -25,11 +25,24 @@ pub fn on_attestation( signed_attestation: SignedAttestation, is_from_block: bool, ) -> Result<(), String> { + #[cfg(feature = "devnet1")] let validator_id = ValidatorIndex(signed_attestation.message.validator_id.0); + #[cfg(feature = "devnet1")] let attestation_slot = signed_attestation.message.data.slot; + #[cfg(feature = "devnet1")] let source_slot = signed_attestation.message.data.source.slot; + #[cfg(feature = "devnet1")] let target_slot = signed_attestation.message.data.target.slot; + #[cfg(feature = "devnet2")] + let validator_id = ValidatorIndex(signed_attestation.validator_id); + #[cfg(feature = "devnet2")] + let attestation_slot = signed_attestation.message.slot; + #[cfg(feature = "devnet2")] + let source_slot = signed_attestation.message.source.slot; + #[cfg(feature = "devnet2")] + let target_slot = signed_attestation.message.target.slot; + // Validate attestation is not from future let curr_slot = store.time / INTERVALS_PER_SLOT; if attestation_slot.0 > curr_slot { @@ -49,6 +62,7 @@ pub fn on_attestation( if is_from_block { // On-chain attestation processing - immediately becomes "known" + #[cfg(feature = "devnet1")] if store .latest_known_attestations .get(&validator_id) @@ -61,14 +75,31 @@ pub fn on_attestation( .insert(validator_id, signed_attestation.clone()); } + #[cfg(feature = "devnet2")] + if store + .latest_known_attestations + .get(&validator_id) + .map_or(true, |existing| existing.message.slot < attestation_slot) + { + store + .latest_known_attestations + .insert(validator_id, signed_attestation.clone()); + } + // Remove from new attestations if superseded if let Some(existing_new) = store.latest_new_attestations.get(&validator_id) { + #[cfg(feature = "devnet1")] if existing_new.message.data.slot <= attestation_slot { store.latest_new_attestations.remove(&validator_id); } + #[cfg(feature = "devnet2")] + if existing_new.message.slot <= attestation_slot { + store.latest_new_attestations.remove(&validator_id); + } } } else { // Network gossip attestation processing - goes to "new" stage + #[cfg(feature = "devnet1")] if store .latest_new_attestations .get(&validator_id) @@ -80,6 +111,17 @@ pub fn on_attestation( .latest_new_attestations .insert(validator_id, signed_attestation); } + + #[cfg(feature = "devnet2")] + if store + .latest_new_attestations + .get(&validator_id) + .map_or(true, |existing| existing.message.slot < attestation_slot) + { + store + .latest_new_attestations + .insert(validator_id, signed_attestation); + } } Ok(()) } @@ -147,43 +189,94 @@ fn process_block_internal( let attestations = &signed_block.message.block.body.attestations; let signatures = &signed_block.signature; - for i in 0.. { - match (attestations.get(i), signatures.get(i)) { - (Ok(attestation), Ok(signature)) => { - let signed_attestation = SignedAttestation { - message: attestation.clone(), - signature: signature.clone(), - }; - on_attestation(store, signed_attestation, true)?; + #[cfg(feature = "devnet1")] + { + for i in 0.. { + match (attestations.get(i), signatures.get(i)) { + (Ok(attestation), Ok(signature)) => { + let signed_attestation = SignedAttestation { + message: attestation.clone(), + signature: signature.clone(), + }; + on_attestation(store, signed_attestation, true)?; + } + _ => break, } - _ => break, } + + // Update head BEFORE processing proposer attestation + update_head(store); + + // Process proposer attestation as gossip (is_from_block=false) + // This ensures it goes to "new" attestations and doesn't immediately affect fork choice + let num_body_attestations = attestations.len_u64(); + + // Get proposer signature or use default if not present (for tests) + use containers::attestation::Signature; + let proposer_signature = signatures + .get(num_body_attestations) + .map(|sig| sig.clone()) + .unwrap_or_else(|_| Signature::default()); + + let proposer_signed_attestation = SignedAttestation { + message: signed_block.message.proposer_attestation.clone(), + signature: proposer_signature, + }; + + // Process proposer attestation as if received via gossip (is_from_block=false) + // This ensures it goes to "new" attestations and doesn't immediately affect fork choice + on_attestation(store, proposer_signed_attestation, false)?; + + Ok(()) } - // Update head BEFORE processing proposer attestation - update_head(store); + #[cfg(feature = "devnet2")] + { + let aggregated_attestations = &signed_block.message.block.body.attestations; + let attestation_signatures = &signed_block.signature.attestation_signatures; + let proposer_attestation = &signed_block.message.proposer_attestation; + + for (aggregated_attestation, aggregated_signature) in aggregated_attestations + .into_iter() + .zip(attestation_signatures) + { + let validator_ids: Vec = aggregated_attestation + .aggregation_bits + .0 + .iter() + .enumerate() + .filter(|(_, bit)| **bit) + .map(|(index, _)| index as u64) + .collect(); - // Process proposer attestation as gossip (is_from_block=false) - // This ensures it goes to "new" attestations and doesn't immediately affect fork choice - let num_body_attestations = attestations.len_u64(); + for (validator_id, signature) in validator_ids.into_iter().zip(aggregated_signature) { + on_attestation( + store, + SignedAttestation { + validator_id, + message: aggregated_attestation.data.clone(), + signature: *signature, + }, + true, + )?; + } + } - // Get proposer signature or use default if not present (for tests) - use containers::attestation::Signature; - let proposer_signature = signatures - .get(num_body_attestations) - .map(|sig| sig.clone()) - .unwrap_or_else(|_| Signature::default()); + // Update head BEFORE processing proposer attestation + update_head(store); - let proposer_signed_attestation = SignedAttestation { - message: signed_block.message.proposer_attestation.clone(), - signature: proposer_signature, - }; + let proposer_signed_attestation = SignedAttestation { + validator_id: proposer_attestation.validator_id.0, + message: proposer_attestation.data.clone(), + signature: signed_block.signature.proposer_signature, + }; - // Process proposer attestation as if received via gossip (is_from_block=false) - // This ensures it goes to "new" attestations and doesn't immediately affect fork choice - on_attestation(store, proposer_signed_attestation, false)?; + // Process proposer attestation as if received via gossip (is_from_block=false) + // This ensures it goes to "new" attestations and doesn't immediately affect fork choice + on_attestation(store, proposer_signed_attestation, false)?; - Ok(()) + Ok(()) + } } fn process_pending_blocks(store: &mut Store, mut roots: Vec) { diff --git a/lean_client/fork_choice/src/store.rs b/lean_client/fork_choice/src/store.rs index bb54574..51b26b6 100644 --- a/lean_client/fork_choice/src/store.rs +++ b/lean_client/fork_choice/src/store.rs @@ -84,7 +84,10 @@ pub fn get_fork_choice_head( // stage 1: accumulate weights by walking up from each attestation's head for attestation in latest_attestations.values() { + #[cfg(feature = "devnet1")] let mut curr = attestation.message.data.head.root; + #[cfg(feature = "devnet2")] + let mut curr = attestation.message.head.root; if let Some(block) = store.blocks.get(&curr) { let mut curr_slot = block.message.block.slot; diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index 0437d9c..85683af 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -259,6 +259,7 @@ fn convert_test_attestation(test_att: &TestAttestation) -> Attestation { } } +#[cfg(feature = "devnet1")] fn convert_test_anchor_block(test_block: &TestAnchorBlock) -> SignedBlockWithAttestation { let mut attestations = ssz::PersistentList::default(); @@ -306,6 +307,7 @@ fn convert_test_anchor_block(test_block: &TestAnchorBlock) -> SignedBlockWithAtt } } +#[cfg(feature = "devnet1")] fn convert_test_block( test_block_with_att: &TestBlockWithAttestation, ) -> SignedBlockWithAttestation { @@ -410,6 +412,7 @@ fn initialize_state_from_test(test_state: &TestAnchorState) -> State { } } +#[cfg(feature = "devnet1")] fn verify_checks( store: &Store, checks: &Option, @@ -498,6 +501,7 @@ fn verify_checks( Ok(()) } +#[cfg(feature = "devnet1")] fn run_single_test(_test_name: &str, test: TestVector) -> Result<(), String> { println!(" Running: {}", test.info.test_id); @@ -630,6 +634,7 @@ fn run_single_test(_test_name: &str, test: TestVector) -> Result<(), String> { Ok(()) } +#[cfg(feature = "devnet1")] fn run_test_vector_file(test_path: &str) -> Result<(), String> { let json_str = std::fs::read_to_string(test_path) .map_err(|e| format!("Failed to read file {}: {}", test_path, e))?; @@ -645,6 +650,7 @@ fn run_test_vector_file(test_path: &str) -> Result<(), String> { } #[test] +#[cfg(feature = "devnet1")] fn test_fork_choice_head_vectors() { let test_dir = "../tests/test_vectors/test_fork_choice/test_fork_choice_head"; @@ -688,6 +694,7 @@ fn test_fork_choice_head_vectors() { } #[test] +#[cfg(feature = "devnet1")] fn test_attestation_processing_vectors() { let test_dir = "../tests/test_vectors/test_fork_choice/test_attestation_processing"; @@ -731,6 +738,7 @@ fn test_attestation_processing_vectors() { } #[test] +#[cfg(feature = "devnet1")] fn test_fork_choice_reorgs_vectors() { let test_dir = "../tests/test_vectors/test_fork_choice/test_fork_choice_reorgs"; @@ -774,6 +782,7 @@ fn test_fork_choice_reorgs_vectors() { } #[test] +#[cfg(feature = "devnet1")] fn test_attestation_target_selection_vectors() { let test_dir = "../tests/test_vectors/test_fork_choice/test_attestation_target_selection"; @@ -817,6 +826,7 @@ fn test_attestation_target_selection_vectors() { } #[test] +#[cfg(feature = "devnet1")] fn test_lexicographic_tiebreaker_vectors() { let test_dir = "../tests/test_vectors/test_fork_choice/test_lexicographic_tiebreaker"; diff --git a/lean_client/fork_choice/tests/unit_tests/votes.rs b/lean_client/fork_choice/tests/unit_tests/votes.rs index 3cdaabb..d6c2ad4 100644 --- a/lean_client/fork_choice/tests/unit_tests/votes.rs +++ b/lean_client/fork_choice/tests/unit_tests/votes.rs @@ -7,6 +7,7 @@ use containers::{ use fork_choice::handlers::on_attestation; use fork_choice::store::{accept_new_attestations, INTERVALS_PER_SLOT}; +#[cfg(feature = "devnet1")] fn create_signed_attestation( validator_id: u64, slot: Slot, @@ -36,6 +37,7 @@ fn create_signed_attestation( } #[test] +#[cfg(feature = "devnet1")] fn test_accept_new_attestations() { let mut store = create_test_store(); @@ -81,6 +83,7 @@ fn test_accept_new_attestations() { } #[test] +#[cfg(feature = "devnet1")] fn test_accept_new_attestations_multiple() { let mut store = create_test_store(); @@ -112,6 +115,7 @@ fn test_accept_new_attestations_empty() { } #[test] +#[cfg(feature = "devnet1")] fn test_on_attestation_lifecycle() { let mut store = create_test_store(); let validator_idx = ValidatorIndex(1); @@ -173,6 +177,7 @@ fn test_on_attestation_lifecycle() { } #[test] +#[cfg(feature = "devnet1")] fn test_on_attestation_future_slot() { let mut store = create_test_store(); let future_slot = Slot(100); // Far in the future @@ -184,6 +189,7 @@ fn test_on_attestation_future_slot() { } #[test] +#[cfg(feature = "devnet1")] fn test_on_attestation_update_vote() { let mut store = create_test_store(); let validator_idx = ValidatorIndex(1); @@ -217,6 +223,7 @@ fn test_on_attestation_update_vote() { } #[test] +#[cfg(feature = "devnet1")] fn test_on_attestation_ignore_old_vote() { let mut store = create_test_store(); let validator_idx = ValidatorIndex(1); @@ -252,6 +259,7 @@ fn test_on_attestation_ignore_old_vote() { } #[test] +#[cfg(feature = "devnet1")] fn test_on_attestation_from_block_supersedes_new() { let mut store = create_test_store(); let validator_idx = ValidatorIndex(1); @@ -273,6 +281,7 @@ fn test_on_attestation_from_block_supersedes_new() { } #[test] +#[cfg(feature = "devnet1")] fn test_on_attestation_newer_from_block_removes_older_new() { let mut store = create_test_store(); let validator_idx = ValidatorIndex(1); diff --git a/lean_client/networking/Cargo.toml b/lean_client/networking/Cargo.toml index c06524e..9025e53 100644 --- a/lean_client/networking/Cargo.toml +++ b/lean_client/networking/Cargo.toml @@ -3,7 +3,13 @@ name = "networking" version = "0.1.0" edition = "2024" +[features] +default = [] +devnet1 = ["containers/devnet1", "env-config/devnet1"] +devnet2 = ["containers/devnet2", "env-config/devnet1"] + [dependencies] +env-config = { path = "../env-config", default-features = false } containers = {workspace = true} alloy-primitives = { workspace = true} libp2p = {workspace = true} diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index b428cb5..aa3b414 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -373,7 +373,10 @@ where } } Ok(GossipsubMessage::Attestation(signed_attestation)) => { + #[cfg(feature = "devnet1")] let slot = signed_attestation.message.data.slot.0; + #[cfg(feature = "devnet2")] + let slot = signed_attestation.message.slot.0; if let Err(err) = self .chain_message_sink @@ -595,7 +598,11 @@ where } } OutboundP2pRequest::GossipAttestation(signed_attestation) => { + #[cfg(feature = "devnet1")] let slot = signed_attestation.message.data.slot.0; + #[cfg(feature = "devnet2")] + let slot = signed_attestation.message.slot.0; + match signed_attestation.to_ssz() { Ok(bytes) => { if let Err(err) = self.publish_to_topic(GossipsubKind::Attestation, bytes) { diff --git a/lean_client/networking/src/types.rs b/lean_client/networking/src/types.rs index b15c737..bbe7cba 100644 --- a/lean_client/networking/src/types.rs +++ b/lean_client/networking/src/types.rs @@ -102,6 +102,7 @@ impl Display for ChainMessage { signed_block_with_attestation.message.block.slot.0 ) } + #[cfg(feature = "devnet1")] ChainMessage::ProcessAttestation { signed_attestation, .. } => { @@ -111,6 +112,16 @@ impl Display for ChainMessage { signed_attestation.message.data.slot.0 ) } + #[cfg(feature = "devnet2")] + ChainMessage::ProcessAttestation { + signed_attestation, .. + } => { + write!( + f, + "ProcessAttestation(slot={})", + signed_attestation.message.slot.0 + ) + } } } } diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index 8ff5eed..86635a0 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use containers::block::BlockSignatures; use containers::ssz::{PersistentList, SszHash}; use containers::{ attestation::{Attestation, AttestationData}, @@ -8,7 +9,7 @@ use containers::{ ssz, state::State, types::{Bytes32, Uint64, ValidatorIndex}, - Slot, + Signature, Slot, }; use fork_choice::{ handlers::{on_attestation, on_block, on_tick}, @@ -208,7 +209,13 @@ async fn main() { block: genesis_block, proposer_attestation: genesis_proposer_attestation, }, + #[cfg(feature = "devnet1")] signature: PersistentList::default(), + #[cfg(feature = "devnet2")] + signature: BlockSignatures { + attestation_signatures: PersistentList::default(), + proposer_signature: Signature::default(), + }, }; let config = Config { genesis_time }; @@ -416,14 +423,29 @@ async fn main() { if last_attestation_slot != Some(current_slot) { let attestations = vs.create_attestations(&store, Slot(current_slot)); for signed_att in attestations { + #[cfg(feature = "devnet1")] let validator_id = signed_att.message.validator_id.0; + #[cfg(feature = "devnet2")] + let validator_id = signed_att.validator_id; info!( slot = current_slot, validator = validator_id, "Broadcasting attestation" ); + #[cfg(feature = "devnet1")] + match on_attestation(&mut store, signed_att.clone(), false) { + Ok(()) => { + if let Err(e) = chain_outbound_sender.send( + OutboundP2pRequest::GossipAttestation(signed_att) + ) { + warn!("Failed to gossip attestation: {}", e); + } + } + Err(e) => warn!("Error processing own attestation: {}", e), + } + #[cfg(feature = "devnet2")] match on_attestation(&mut store, signed_att.clone(), false) { Ok(()) => { if let Err(e) = chain_outbound_sender.send( @@ -519,10 +541,24 @@ async fn main() { should_gossip, .. } => { + #[cfg(feature = "devnet1")] let att_slot = signed_attestation.message.data.slot.0; + #[cfg(feature = "devnet1")] let source_slot = signed_attestation.message.data.source.slot.0; + #[cfg(feature = "devnet1")] let target_slot = signed_attestation.message.data.target.slot.0; + #[cfg(feature = "devnet1")] let validator_id = signed_attestation.message.validator_id.0; + + #[cfg(feature = "devnet2")] + let att_slot = signed_attestation.message.slot.0; + #[cfg(feature = "devnet2")] + let source_slot = signed_attestation.message.source.slot.0; + #[cfg(feature = "devnet2")] + let target_slot = signed_attestation.message.target.slot.0; + #[cfg(feature = "devnet2")] + let validator_id = signed_attestation.validator_id; + info!( slot = att_slot, source_slot = source_slot, diff --git a/lean_client/validator/Cargo.toml b/lean_client/validator/Cargo.toml index b658c48..8311b9d 100644 --- a/lean_client/validator/Cargo.toml +++ b/lean_client/validator/Cargo.toml @@ -6,8 +6,11 @@ edition = "2021" [features] default = ["xmss-signing"] xmss-signing = ["leansig"] +devnet1 = ["containers/devnet1", "fork-choice/devnet1", "env-config/devnet1"] +devnet2 = ["containers/devnet2", "fork-choice/devnet2", "env-config/devnet1"] [dependencies] +env-config = { path = "../env-config", default-features = false } serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" containers = { path = "../containers" } diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index 6c6a4a4..752cda8 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -2,12 +2,16 @@ use std::collections::HashMap; use std::path::Path; +use containers::attestation::AggregatedAttestations; +#[cfg(feature = "devnet2")] +use containers::attestation::NaiveAggregatedSignature; +use containers::block::BlockSignatures; use containers::{ attestation::{Attestation, AttestationData, Signature, SignedAttestation}, block::{hash_tree_root, BlockWithAttestation, SignedBlockWithAttestation}, checkpoint::Checkpoint, types::{Uint64, ValidatorIndex}, - Slot, + AggregatedAttestation, Slot, }; use fork_choice::store::{get_proposal_head, get_vote_target, Store}; use tracing::{info, warn}; @@ -172,7 +176,10 @@ impl ValidatorService { .latest_new_attestations .values() .filter(|att| { + #[cfg(feature = "devnet1")] let data = &att.message.data; + #[cfg(feature = "devnet2")] + let data = &att.message; // Source must match the parent state's justified checkpoint (not store's!) let source_matches = data.source == parent_state.latest_justified; // Target must be strictly after source @@ -184,11 +191,18 @@ impl ValidatorService { }) .collect(); + #[cfg(feature = "devnet1")] let valid_attestations: Vec = valid_signed_attestations .iter() .map(|att| att.message.clone()) .collect(); + #[cfg(feature = "devnet2")] + let valid_attestations: Vec = valid_signed_attestations + .iter() + .map(|att| att.message.clone()) + .collect(); + info!( slot = slot.0, valid_attestations = valid_attestations.len(), @@ -197,6 +211,7 @@ impl ValidatorService { ); // Build block with collected attestations (empty body - attestations go to state) + #[cfg(feature = "devnet1")] let (block, _post_state, _collected_atts, sigs) = parent_state.build_block( slot, proposer_index, @@ -205,13 +220,43 @@ impl ValidatorService { None, None, )?; + #[cfg(feature = "devnet2")] + let (block, _post_state, _collected_atts, sigs) = { + let valid_attestations: Vec = valid_attestations + .iter() + .map(|data| Attestation { + validator_id: Uint64(0), // Placeholder, real validator IDs should be used + data: data.clone(), + }) + .collect(); + parent_state.build_block( + slot, + proposer_index, + parent_root, + Some(valid_attestations), + None, + None, + )? + }; // Collect signatures from the attestations we included + #[cfg(feature = "devnet1")] let mut signatures = sigs; + #[cfg(feature = "devnet2")] + let mut signatures = sigs.attestation_signatures; for signed_att in &valid_signed_attestations { + #[cfg(feature = "devnet1")] signatures .push(signed_att.signature.clone()) .map_err(|e| format!("Failed to add attestation signature: {:?}", e))?; + #[cfg(feature = "devnet2")] + { + // TODO: Use real aggregation instead of naive placeholder when spec is more up to date + let aggregated_sig: NaiveAggregatedSignature = NaiveAggregatedSignature::default(); + signatures + .push(aggregated_sig) + .map_err(|e| format!("Failed to add attestation signature: {:?}", e))?; + } } info!( @@ -231,9 +276,19 @@ impl ValidatorService { match key_manager.sign(proposer_index.0, epoch, &message.0.into()) { Ok(sig) => { + #[cfg(feature = "devnet1")] signatures .push(sig) .map_err(|e| format!("Failed to add proposer signature: {:?}", e))?; + #[cfg(feature = "devnet2")] + { + // TODO: Use real aggregation instead of naive placeholder when spec is more up to date + let aggregated_sig: NaiveAggregatedSignature = + NaiveAggregatedSignature::default(); + signatures + .push(aggregated_sig) + .map_err(|e| format!("Failed to add proposer signature: {:?}", e))?; + } info!(proposer = proposer_index.0, "Signed proposer attestation"); } Err(e) => { @@ -250,7 +305,13 @@ impl ValidatorService { block, proposer_attestation, }, + #[cfg(feature = "devnet1")] signature: signatures, + #[cfg(feature = "devnet2")] + signature: BlockSignatures { + attestation_signatures: signatures, + proposer_signature: Signature::default(), + }, }; Ok(signed_block) @@ -290,6 +351,7 @@ impl ValidatorService { .validator_indices .iter() .filter_map(|&idx| { + #[cfg(feature = "devnet1")] let attestation = Attestation { validator_id: Uint64(idx), data: AttestationData { @@ -300,6 +362,14 @@ impl ValidatorService { }, }; + #[cfg(feature = "devnet2")] + let attestation = AttestationData { + slot, + head: head_checkpoint.clone(), + target: vote_target.clone(), + source: store.latest_justified.clone(), + }; + let signature = if let Some(ref key_manager) = self.key_manager { // Sign with XMSS let message = hash_tree_root(&attestation); @@ -337,10 +407,24 @@ impl ValidatorService { Signature::default() }; - Some(SignedAttestation { - message: attestation, - signature, - }) + { + #[cfg(feature = "devnet1")] + { + Some(SignedAttestation { + message: attestation, + signature, + }) + } + + #[cfg(feature = "devnet2")] + { + Some(SignedAttestation { + validator_id: idx, + message: attestation, + signature, + }) + } + } }) .collect() } From 89647c97b9252d91487cc4091d0d3b516be070a0 Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:26:50 +0200 Subject: [PATCH 07/48] fixes --- lean_client/Makefile | 2 +- lean_client/chain/src/config.rs | 5 ---- lean_client/chain/src/lib.rs | 2 +- lean_client/containers/src/attestation.rs | 2 +- .../unit_tests/attestation_aggregation.rs | 30 +++++++++++++------ .../containers/tests/unit_tests/mod.rs | 2 +- lean_client/env-config/src/lib.rs | 2 +- lean_client/networking/src/network/service.rs | 2 +- 8 files changed, 27 insertions(+), 20 deletions(-) diff --git a/lean_client/Makefile b/lean_client/Makefile index 507f78a..8b3c356 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -39,7 +39,7 @@ check-format: .PHONY: test test: - cargo test --workspace --all-features --no-fail-fast + cargo test --workspace --features devnet2,xmss-signing --no-fail-fast .PHONY: build build: diff --git a/lean_client/chain/src/config.rs b/lean_client/chain/src/config.rs index 71b4df1..6dd26fb 100644 --- a/lean_client/chain/src/config.rs +++ b/lean_client/chain/src/config.rs @@ -15,11 +15,6 @@ impl BasisPoint { pub fn get(&self) -> u64 { self.0 } - - #[inline] - pub fn get(&self) -> u64 { - self.0 - } } #[derive(Clone, Debug)] diff --git a/lean_client/chain/src/lib.rs b/lean_client/chain/src/lib.rs index 12cf630..9496841 100644 --- a/lean_client/chain/src/lib.rs +++ b/lean_client/chain/src/lib.rs @@ -1,2 +1,2 @@ mod config; -pub use config::ChainConfig; \ No newline at end of file +pub use config::ChainConfig; diff --git a/lean_client/containers/src/attestation.rs b/lean_client/containers/src/attestation.rs index 9c3537c..6779b0f 100644 --- a/lean_client/containers/src/attestation.rs +++ b/lean_client/containers/src/attestation.rs @@ -126,7 +126,7 @@ pub struct AggregatedAttestation { /// Bitfield indicating which validators participated in the aggregation. pub aggregation_bits: AggregationBits, /// Combined attestation data similar to the beacon chain format. - /// + /// /// Multiple validator attestations are aggregated here without the complexity of /// committee assignments. pub data: AttestationData, diff --git a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs index 285aa46..72d48b4 100644 --- a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs +++ b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs @@ -1,10 +1,12 @@ #[cfg(feature = "devnet2")] #[cfg(test)] mod tests { - use containers::attestation::{AggregatedAttestation, AggregationBits, Attestation, AttestationData}; - use containers::{Bytes32, Uint64}; + use containers::attestation::{ + AggregatedAttestation, AggregationBits, Attestation, AttestationData, + }; use containers::checkpoint::Checkpoint; use containers::slot::Slot; + use containers::{Bytes32, Uint64}; #[test] fn test_aggregated_attestation_structure() { @@ -21,17 +23,22 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - } + }, }; let bits = AggregationBits::from_validator_indices(&vec![2, 7]); let agg = AggregatedAttestation { aggregation_bits: bits.clone(), - data: att_data.clone() + data: att_data.clone(), }; let indices = agg.aggregation_bits.to_validator_indices(); - assert_eq!(indices.into_iter().collect::>(), vec![2, 7].into_iter().collect()); + assert_eq!( + indices + .into_iter() + .collect::>(), + vec![2, 7].into_iter().collect() + ); assert_eq!(agg.data, att_data); } @@ -50,7 +57,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - } + }, }; let att_data2 = AttestationData { slot: Slot(6), @@ -65,7 +72,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(3), - } + }, }; let attestations = vec![ @@ -88,7 +95,12 @@ mod tests { let agg1 = aggregated.iter().find(|agg| agg.data == att_data1).unwrap(); let validator_ids1 = agg1.aggregation_bits.to_validator_indices(); - assert_eq!(validator_ids1.into_iter().collect::>(), vec![1, 3].into_iter().collect()); + assert_eq!( + validator_ids1 + .into_iter() + .collect::>(), + vec![1, 3].into_iter().collect() + ); let agg2 = aggregated.iter().find(|agg| agg.data == att_data2).unwrap(); let validator_ids2 = agg2.aggregation_bits.to_validator_indices(); @@ -116,7 +128,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - } + }, }; let attestations = vec![Attestation { diff --git a/lean_client/containers/tests/unit_tests/mod.rs b/lean_client/containers/tests/unit_tests/mod.rs index b9f442f..1bef390 100644 --- a/lean_client/containers/tests/unit_tests/mod.rs +++ b/lean_client/containers/tests/unit_tests/mod.rs @@ -1,7 +1,7 @@ // tests/unit_tests/mod.rs +mod attestation_aggregation; mod common; mod state_basic; mod state_justifications; mod state_process; mod state_transition; -mod attestation_aggregation; diff --git a/lean_client/env-config/src/lib.rs b/lean_client/env-config/src/lib.rs index 972005d..109ac2d 100644 --- a/lean_client/env-config/src/lib.rs +++ b/lean_client/env-config/src/lib.rs @@ -1 +1 @@ -// Empty on purpose \ No newline at end of file +// Empty on purpose diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index aa3b414..a78bd93 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -602,7 +602,7 @@ where let slot = signed_attestation.message.data.slot.0; #[cfg(feature = "devnet2")] let slot = signed_attestation.message.slot.0; - + match signed_attestation.to_ssz() { Ok(bytes) => { if let Err(err) = self.publish_to_topic(GossipsubKind::Attestation, bytes) { From 7a227a07c4ffd74f3b87b533320e5100711dbefe Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:26:12 +0200 Subject: [PATCH 08/48] Set default logging level to INFO --- lean_client/src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index 86635a0..002beca 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -29,6 +29,7 @@ use tokio::{ task, time::{interval, Duration}, }; +use tracing::level_filters::LevelFilter; use tracing::{debug, info, warn}; use validator::{ValidatorConfig, ValidatorService}; @@ -134,7 +135,11 @@ struct Args { #[tokio::main] async fn main() { tracing_subscriber::fmt() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_env_filter( + tracing_subscriber::EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(), + ) .init(); let args = Args::parse(); From 4d7dc68171ac9fca1928caea28d28a5cc1202b91 Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:36:13 +0200 Subject: [PATCH 09/48] Makefile script for test vector generation --- lean_client/Makefile | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lean_client/Makefile b/lean_client/Makefile index 8b3c356..43c4534 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -29,10 +29,13 @@ endif all: build check-format test +### Linting +## Format source code. .PHONY: format format: cargo fmt +## Check source code formatting. Issues can be automatically fixed with `make format`. .PHONY: check-format check-format: cargo fmt --check @@ -41,6 +44,18 @@ check-format: test: cargo test --workspace --features devnet2,xmss-signing --no-fail-fast +.PHONY: generate-test-vectors +generate-test-vectors: + @rm -rf spec && \ + mkdir -p spec && \ + cd spec && \ + git init && \ + git remote add origin https://github.com/leanEthereum/leanSpec.git && \ + git fetch --depth 1 origin $(LEAN_SPEC_COMMIT) && \ + git switch --detach FETCH_HEAD + cd spec && uv run fill --clean --fork=devnet + cp -r ./spec/fixtures/consensus/* ./tests/test_vectors/ + .PHONY: build build: cargo build --release @@ -57,11 +72,6 @@ aarch64-unknown-linux-gnu: ./target/aarch64-unknown-linux-gnu/release/lean_clien ./target/aarch64-unknown-linux-gnu/release/lean_client: cross build --bin lean_client --target aarch64-unknown-linux-gnu --profile release -DOCKER_REPO ?= sifrai/lean -DOCKER_TAG ?= unstable -COMMIT_SHA := $(shell git rev-parse HEAD) -BUILD_DATE := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') - .PHONY: release release: docker From 4a73cfafc47f66b35865c1f7a0640177a451cf56 Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:51:12 +0200 Subject: [PATCH 10/48] Updates for devnet-2 Various updates, squashed from feature/multisig-aggregation branch. Co-authored-by: Julius Mieliauskas Co-authored-by: Nojus Sandovas Co-authored-by: Darius Spr <108625236+Dariusspr@users.noreply.github.com> Co-authored-by: LiudasBaronas1 <144480589+LiudasBaronas1@users.noreply.github.com> --- lean_client/Cargo.lock | 1715 ++++++++++++----- lean_client/Cargo.toml | 8 +- lean_client/ENVIRONMENT_SELECTION.md | 26 - lean_client/Makefile | 2 +- lean_client/chain/src/config.rs | 1 + lean_client/containers/Cargo.toml | 15 +- lean_client/containers/src/attestation.rs | 247 ++- lean_client/containers/src/block.rs | 237 +-- lean_client/containers/src/lib.rs | 6 +- lean_client/containers/src/public_key.rs | 207 ++ lean_client/containers/src/serde_helpers.rs | 295 +-- lean_client/containers/src/signature.rs | 121 ++ lean_client/containers/src/state.rs | 788 ++++---- lean_client/containers/src/validator.rs | 77 +- .../tests/test_vectors/block_processing.rs | 11 +- .../containers/tests/test_vectors/mod.rs | 4 + .../containers/tests/test_vectors/runner.rs | 35 +- .../unit_tests/attestation_aggregation.rs | 1 - .../containers/tests/unit_tests/common.rs | 24 +- .../containers/tests/unit_tests/mod.rs | 7 +- .../tests/unit_tests/state_basic.rs | 4 + .../tests/unit_tests/state_justifications.rs | 4 + .../tests/unit_tests/state_process.rs | 69 - .../tests/unit_tests/state_transition.rs | 35 +- lean_client/env-config/Cargo.toml | 4 - lean_client/fork_choice/Cargo.toml | 11 +- lean_client/fork_choice/src/handlers.rs | 188 +- lean_client/fork_choice/src/store.rs | 96 +- .../tests/fork_choice_test_vectors.rs | 1090 +++-------- .../fork_choice/tests/unit_tests/votes.rs | 455 ++--- lean_client/networking/Cargo.toml | 2 - lean_client/networking/src/network/service.rs | 6 - lean_client/networking/src/types.rs | 11 - lean_client/src/main.rs | 36 +- .../test_invalid_signature.json | 58 +- ...test_proposer_and_attester_signatures.json | 1635 +++++++++++----- .../test_proposer_signature.json | 1326 +++++++++++-- lean_client/validator/Cargo.toml | 3 - lean_client/validator/src/keys.rs | 2 +- lean_client/validator/src/lib.rs | 114 +- 40 files changed, 5500 insertions(+), 3476 deletions(-) delete mode 100644 lean_client/ENVIRONMENT_SELECTION.md create mode 100644 lean_client/containers/src/public_key.rs create mode 100644 lean_client/containers/src/signature.rs diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index 5d46d69..3aad917 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -51,13 +51,25 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] +[[package]] +name = "air" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "multilinear-toolkit", + "p3-air", + "p3-util 0.3.0", + "tracing", + "utils", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -66,9 +78,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-primitives" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" +checksum = "f6a0fb18dd5fb43ec5f0f6a20be1ce0287c79825827de5744afaa6c957737c33" dependencies = [ "alloy-rlp", "bytes", @@ -76,14 +88,15 @@ dependencies = [ "const-hex", "derive_more", "foldhash 0.2.0", - "hashbrown 0.16.0", - "indexmap 2.11.4", + "hashbrown 0.16.1", + "indexmap 2.13.0", "itoa", "k256", "keccak-asm", "paste", "proptest", "rand 0.9.2", + "rapidhash", "ruint", "rustc-hash", "serde", @@ -110,6 +123,15 @@ 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.21" @@ -142,22 +164,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -169,7 +191,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arithmetic" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#5bdc78763c8959ad689c79d51d7d59978460bb1e" +source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" dependencies = [ "easy-ext", "typenum", @@ -260,7 +282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -298,7 +320,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -388,7 +410,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -400,7 +422,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "synstructure 0.13.2", ] @@ -412,7 +434,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -448,7 +470,7 @@ dependencies = [ "polling", "rustix", "slab", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -459,7 +481,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -514,7 +536,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -523,6 +545,20 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "backend" +version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +dependencies = [ + "fiat-shamir", + "itertools 0.14.0", + "p3-field 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", + "rayon", + "tracing", +] + [[package]] name = "base-x" version = "0.2.11" @@ -553,9 +589,18 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bincode" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] [[package]] name = "bit-set" @@ -586,9 +631,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -611,15 +656,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -646,9 +682,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-slice-cast" @@ -664,18 +700,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.2.38" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -683,9 +719,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -746,9 +782,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -756,9 +792,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -775,14 +811,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "colorchoice" @@ -790,6 +826,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -801,9 +846,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6407bff74dea37e0fa3dc1c1c974e5d46405f0c987bf9997a0762adce71eda6" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" dependencies = [ "cfg-if", "cpufeatures", @@ -825,9 +870,9 @@ checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -843,12 +888,25 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constraints-folder" +version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +dependencies = [ + "fiat-shamir", + "p3-air", + "p3-field 0.3.0", +] + [[package]] name = "containers" version = "0.1.0" dependencies = [ + "alloy-primitives", + "anyhow", "env-config", "hex", + "lean-multisig", "leansig", "pretty_assertions", "rstest", @@ -964,9 +1022,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1006,7 +1064,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1030,7 +1088,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1041,7 +1099,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1060,15 +1118,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "data-encoding-macro" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1076,12 +1134,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1121,12 +1179,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -1159,7 +1217,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.114", "unicode-xid", ] @@ -1184,7 +1242,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -1229,14 +1287,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "dtoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" [[package]] name = "dyn-clone" @@ -1298,7 +1356,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1354,7 +1412,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1374,7 +1432,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1394,7 +1452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1517,11 +1575,22 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fiat-shamir" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/fiat-shamir.git#bcf23c766f2e930acf11e68777449483a55af077" +dependencies = [ + "p3-challenger 0.3.0", + "p3-field 0.3.0", + "p3-koala-bear 0.3.0", + "serde", +] + [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "fixed-hash" @@ -1559,12 +1628,7 @@ version = "0.1.0" dependencies = [ "containers", "env-config", - "serde", - "serde_json", "ssz", - "ssz_derive", - "ssz_rs", - "typenum", ] [[package]] @@ -1659,7 +1723,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1723,28 +1787,28 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] @@ -1777,9 +1841,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", @@ -1787,7 +1851,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.11.4", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -1822,23 +1886,24 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "foldhash 0.2.0", "serde", + "serde_core", ] [[package]] name = "hashing" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#5bdc78763c8959ad689c79d51d7d59978460bb1e" +source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" dependencies = [ "ethereum-types", "generic-array", "hex-literal", - "sha2 0.10.9 (git+https://github.com/grandinetech/universal-precompiles.git?tag=sha2-v0.10.9-up.1)", + "sha2 0.10.9 (git+https://github.com/grandinetech/universal-precompiles.git?tag=sha2-v0.10.9-up.2)", ] [[package]] @@ -1873,9 +1938,9 @@ dependencies = [ [[package]] name = "hex-literal" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" [[package]] name = "hex_fmt" @@ -1902,7 +1967,7 @@ dependencies = [ "rand 0.9.2", "ring", "socket2 0.5.10", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -1925,7 +1990,7 @@ dependencies = [ "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -1950,12 +2015,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1990,9 +2054,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -2012,9 +2076,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "bytes", "futures-channel", @@ -2043,7 +2107,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.0", + "windows-core 0.62.2", ] [[package]] @@ -2103,9 +2167,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -2117,9 +2181,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -2252,7 +2316,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2268,12 +2332,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -2349,15 +2413,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2402,6 +2466,20 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lean-multisig" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "clap", + "multilinear-toolkit", + "p3-koala-bear 0.3.0", + "poseidon_circuit", + "rec_aggregation", + "whir-p3", + "xmss", +] + [[package]] name = "lean_client" version = "0.1.0" @@ -2411,7 +2489,7 @@ dependencies = [ "containers", "fork-choice", "hex", - "libp2p-identity 0.2.12", + "libp2p-identity 0.2.13", "networking", "tokio", "tracing", @@ -2419,31 +2497,120 @@ dependencies = [ "validator", ] +[[package]] +name = "lean_compiler" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "air", + "lean_vm", + "lookup", + "multilinear-toolkit", + "p3-air", + "p3-challenger 0.3.0", + "p3-koala-bear 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "pest", + "pest_derive", + "rand 0.9.2", + "sub_protocols", + "tracing", + "utils", + "whir-p3", + "xmss", +] + +[[package]] +name = "lean_prover" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "air", + "itertools 0.14.0", + "lean_compiler", + "lean_vm", + "lookup", + "multilinear-toolkit", + "p3-air", + "p3-challenger 0.3.0", + "p3-koala-bear 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "pest", + "pest_derive", + "poseidon_circuit", + "rand 0.9.2", + "sub_protocols", + "tracing", + "utils", + "whir-p3", + "witness_generation", + "xmss", +] + +[[package]] +name = "lean_vm" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "air", + "colored", + "derive_more", + "itertools 0.14.0", + "lookup", + "multilinear-toolkit", + "num_enum", + "p3-air", + "p3-challenger 0.3.0", + "p3-koala-bear 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "pest", + "pest_derive", + "rand 0.9.2", + "strum", + "sub_protocols", + "thiserror 2.0.17", + "tracing", + "utils", + "whir-p3", + "xmss", +] + [[package]] name = "leansig" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanSig?branch=main#d9610e7fbbc75197f134e065df79acc630994706" +source = "git+https://github.com/leanEthereum/leanSig?branch=main#ae12a5feb25d917c42b6466444ebd56ec115a629" dependencies = [ "dashmap", "ethereum_ssz", "num-bigint", "num-traits", - "p3-baby-bear", - "p3-field", - "p3-koala-bear", - "p3-symmetric", + "p3-baby-bear 0.4.1", + "p3-field 0.4.1", + "p3-koala-bear 0.4.1", + "p3-symmetric 0.4.1", "rand 0.9.2", "rayon", "serde", "sha3", - "thiserror 2.0.16", + "thiserror 2.0.17", ] +[[package]] +name = "lib-c" +version = "0.13.0" +source = "git+https://github.com/0xPolygonHermez/zisk.git?tag=v0.13.0#ea1ed4c518992a170fc59ec19f1228eb4829a9e1" + [[package]] name = "libc" -version = "0.2.175" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libm" @@ -2461,14 +2628,14 @@ dependencies = [ "either", "futures", "futures-timer", - "getrandom 0.2.16", + "getrandom 0.2.17", "libp2p-allow-block-list", "libp2p-connection-limits", - "libp2p-core 0.43.1", + "libp2p-core 0.43.2", "libp2p-dns", "libp2p-gossipsub", "libp2p-identify", - "libp2p-identity 0.2.12", + "libp2p-identity 0.2.13", "libp2p-mdns", "libp2p-metrics", "libp2p-noise", @@ -2481,7 +2648,7 @@ dependencies = [ "multiaddr 0.18.2", "pin-project", "rw-stream-sink 0.4.0", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -2490,8 +2657,8 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16ccf824ee859ca83df301e1c0205270206223fd4b1f2e512a693e1912a8f4a" dependencies = [ - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-swarm", ] @@ -2501,8 +2668,8 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18b8b607cf3bfa2f8c57db9c7d8569a315d5cc0a282e6bfd5ebfc0a9840b2a0" dependencies = [ - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-swarm", ] @@ -2536,15 +2703,15 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.43.1" +version = "0.43.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d28e2d2def7c344170f5c6450c0dbe3dfef655610dbfde2f6ac28a527abbe36" +checksum = "249128cd37a2199aff30a7675dffa51caf073b51aa612d2f544b19932b9aebca" dependencies = [ "either", "fnv", "futures", "futures-timer", - "libp2p-identity 0.2.12", + "libp2p-identity 0.2.13", "multiaddr 0.18.2", "multihash 0.19.3", "multistream-select 0.13.0", @@ -2553,7 +2720,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "rw-stream-sink 0.4.0", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "unsigned-varint 0.8.0", "web-time", @@ -2568,8 +2735,8 @@ dependencies = [ "async-trait", "futures", "hickory-resolver", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "parking_lot", "smallvec", "tracing", @@ -2590,11 +2757,11 @@ dependencies = [ "fnv", "futures", "futures-timer", - "getrandom 0.2.16", + "getrandom 0.2.17", "hashlink", "hex_fmt", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", @@ -2616,13 +2783,13 @@ dependencies = [ "futures", "futures-bounded", "futures-timer", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-swarm", "quick-protobuf", "quick-protobuf-codec", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -2646,9 +2813,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3104e13b51e4711ff5738caa1fb54467c8604c2e94d607e27745bcf709068774" +checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" dependencies = [ "asn1_der", "bs58 0.5.1", @@ -2659,7 +2826,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "sha2 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "zeroize", ] @@ -2673,8 +2840,8 @@ dependencies = [ "futures", "hickory-proto", "if-watch", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-swarm", "rand 0.8.5", "smallvec", @@ -2690,10 +2857,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "805a555148522cb3414493a5153451910cb1a146c53ffbf4385708349baf62b7" dependencies = [ "futures", - "libp2p-core 0.43.1", + "libp2p-core 0.43.2", "libp2p-gossipsub", "libp2p-identify", - "libp2p-identity 0.2.12", + "libp2p-identity 0.2.13", "libp2p-swarm", "pin-project", "prometheus-client", @@ -2727,15 +2894,15 @@ dependencies = [ "asynchronous-codec 0.7.0", "bytes", "futures", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "multiaddr 0.18.2", "multihash 0.19.3", "quick-protobuf", "rand 0.8.5", "snow", "static_assertions", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "x25519-dalek", "zeroize", @@ -2750,15 +2917,15 @@ dependencies = [ "futures", "futures-timer", "if-watch", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-tls", "quinn", "rand 0.8.5", "ring", "rustls", "socket2 0.5.10", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -2772,8 +2939,8 @@ dependencies = [ "async-trait", "futures", "futures-bounded", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-swarm", "rand 0.8.5", "smallvec", @@ -2790,8 +2957,8 @@ dependencies = [ "fnv", "futures", "futures-timer", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "libp2p-swarm-derive", "lru", "multistream-select 0.13.0", @@ -2810,7 +2977,7 @@ checksum = "dd297cf53f0cb3dee4d2620bb319ae47ef27c702684309f682bdb7e55a18ae9c" dependencies = [ "heck", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2823,7 +2990,7 @@ dependencies = [ "futures-timer", "if-watch", "libc", - "libp2p-core 0.43.1", + "libp2p-core 0.43.2", "socket2 0.5.10", "tokio", "tracing", @@ -2837,13 +3004,13 @@ checksum = "96ff65a82e35375cbc31ebb99cacbbf28cb6c4fefe26bf13756ddcf708d40080" dependencies = [ "futures", "futures-rustls", - "libp2p-core 0.43.1", - "libp2p-identity 0.2.12", + "libp2p-core 0.43.2", + "libp2p-identity 0.2.13", "rcgen", "ring", "rustls", "rustls-webpki", - "thiserror 2.0.16", + "thiserror 2.0.17", "x509-parser", "yasna", ] @@ -2857,7 +3024,7 @@ dependencies = [ "futures", "futures-timer", "igd-next", - "libp2p-core 0.43.1", + "libp2p-core 0.43.2", "libp2p-swarm", "tokio", "tracing", @@ -2871,8 +3038,8 @@ checksum = "f15df094914eb4af272acf9adaa9e287baa269943f32ea348ba29cfb9bfc60d8" dependencies = [ "either", "futures", - "libp2p-core 0.43.1", - "thiserror 2.0.16", + "libp2p-core 0.43.2", + "thiserror 2.0.17", "tracing", "yamux 0.12.1", "yamux 0.13.8", @@ -2901,9 +3068,24 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lookup" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "multilinear-toolkit", + "p3-challenger 0.3.0", + "p3-koala-bear 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", + "tracing", + "utils", + "whir-p3", +] [[package]] name = "lru" @@ -2942,9 +3124,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" @@ -2954,20 +3136,20 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.61.1", + "wasi", + "windows-sys 0.61.2", ] [[package]] name = "moka" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" +checksum = "a3dec6bd31b08944e08b58fd99373893a6c17054d6f3ea5006cc894f4f4eee2a" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -2975,7 +3157,6 @@ dependencies = [ "equivalent", "parking_lot", "portable-atomic", - "rustc_version 0.4.1", "smallvec", "tagptr", "uuid", @@ -3015,7 +3196,7 @@ dependencies = [ "arrayref", "byteorder", "data-encoding", - "libp2p-identity 0.2.12", + "libp2p-identity 0.2.13", "multibase", "multihash 0.19.3", "percent-encoding", @@ -3072,6 +3253,20 @@ dependencies = [ "synstructure 0.12.6", ] +[[package]] +name = "multilinear-toolkit" +version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +dependencies = [ + "backend", + "constraints-folder", + "fiat-shamir", + "p3-field 0.3.0", + "p3-util 0.3.0", + "rayon", + "sumcheck", +] + [[package]] name = "multistream-select" version = "0.12.1" @@ -3148,7 +3343,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -3178,7 +3373,7 @@ dependencies = [ "futures", "hex", "libp2p", - "libp2p-identity 0.2.12", + "libp2p-identity 0.2.13", "libp2p-mplex", "parking_lot", "rand 0.8.5", @@ -3226,7 +3421,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -3274,6 +3469,28 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "oid-registry" version = "0.8.1" @@ -3305,176 +3522,399 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "p3-air" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +dependencies = [ + "p3-field 0.3.0", + "p3-matrix 0.3.0", +] + [[package]] name = "p3-baby-bear" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ - "p3-field", - "p3-mds", - "p3-monty-31", - "p3-poseidon2", - "p3-symmetric", + "p3-field 0.3.0", + "p3-mds 0.3.0", + "p3-monty-31 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", "rand 0.9.2", ] [[package]] -name = "p3-dft" +name = "p3-baby-bear" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "p3-challenger 0.4.1", + "p3-field 0.4.1", + "p3-mds 0.4.1", + "p3-monty-31 0.4.1", + "p3-poseidon2 0.4.1", + "p3-symmetric 0.4.1", + "rand 0.9.2", +] + +[[package]] +name = "p3-challenger" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ - "itertools 0.14.0", - "p3-field", - "p3-matrix", - "p3-maybe-rayon", - "p3-util", - "spin", + "p3-field 0.3.0", + "p3-maybe-rayon 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", "tracing", ] [[package]] -name = "p3-field" -version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +name = "p3-challenger" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" dependencies = [ - "itertools 0.14.0", - "num-bigint", - "p3-maybe-rayon", - "p3-util", - "paste", - "rand 0.9.2", - "serde", + "p3-field 0.4.1", + "p3-maybe-rayon 0.4.1", + "p3-monty-31 0.4.1", + "p3-symmetric 0.4.1", + "p3-util 0.4.1", "tracing", ] [[package]] -name = "p3-koala-bear" +name = "p3-commit" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ - "p3-field", - "p3-monty-31", - "p3-poseidon2", - "p3-symmetric", - "rand 0.9.2", + "itertools 0.14.0", + "p3-challenger 0.3.0", + "p3-dft 0.3.0", + "p3-field 0.3.0", + "p3-matrix 0.3.0", + "p3-util 0.3.0", + "serde", ] [[package]] -name = "p3-matrix" +name = "p3-dft" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ "itertools 0.14.0", - "p3-field", - "p3-maybe-rayon", - "p3-util", - "rand 0.9.2", - "serde", + "p3-field 0.3.0", + "p3-matrix 0.3.0", + "p3-maybe-rayon 0.3.0", + "p3-util 0.3.0", "tracing", - "transpose", ] [[package]] -name = "p3-maybe-rayon" -version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +name = "p3-dft" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "itertools 0.14.0", + "p3-field 0.4.1", + "p3-matrix 0.4.1", + "p3-maybe-rayon 0.4.1", + "p3-util 0.4.1", + "spin", + "tracing", +] [[package]] -name = "p3-mds" +name = "p3-field" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ - "p3-dft", - "p3-field", - "p3-symmetric", - "p3-util", + "itertools 0.14.0", + "num-bigint", + "p3-maybe-rayon 0.3.0", + "p3-util 0.3.0", + "paste", "rand 0.9.2", + "serde", + "tracing", ] [[package]] -name = "p3-monty-31" -version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +name = "p3-field" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" dependencies = [ "itertools 0.14.0", "num-bigint", - "p3-dft", - "p3-field", - "p3-matrix", - "p3-maybe-rayon", - "p3-mds", - "p3-poseidon2", - "p3-symmetric", - "p3-util", + "p3-maybe-rayon 0.4.1", + "p3-util 0.4.1", "paste", "rand 0.9.2", "serde", - "spin", "tracing", - "transpose", ] [[package]] -name = "p3-poseidon2" +name = "p3-interpolation" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ - "p3-field", - "p3-mds", - "p3-symmetric", - "p3-util", - "rand 0.9.2", + "p3-field 0.3.0", + "p3-matrix 0.3.0", + "p3-maybe-rayon 0.3.0", + "p3-util 0.3.0", ] [[package]] -name = "p3-symmetric" +name = "p3-koala-bear" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ "itertools 0.14.0", - "p3-field", + "num-bigint", + "p3-field 0.3.0", + "p3-monty-31 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", "serde", ] [[package]] -name = "p3-util" +name = "p3-koala-bear" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "p3-challenger 0.4.1", + "p3-field 0.4.1", + "p3-monty-31 0.4.1", + "p3-poseidon2 0.4.1", + "p3-symmetric 0.4.1", + "rand 0.9.2", +] + +[[package]] +name = "p3-matrix" version = "0.3.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=a33a312#a33a31274a5e78bb5fbe3f82ffd2c294e17fa830" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ + "itertools 0.14.0", + "p3-field 0.3.0", + "p3-maybe-rayon 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", "serde", + "tracing", + "transpose", ] [[package]] -name = "parity-scale-codec" -version = "3.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +name = "p3-matrix" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "const_format", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "rustversion", + "itertools 0.14.0", + "p3-field 0.4.1", + "p3-maybe-rayon 0.4.1", + "p3-util 0.4.1", + "rand 0.9.2", "serde", + "tracing", + "transpose", ] [[package]] -name = "parity-scale-codec-derive" -version = "3.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +name = "p3-maybe-rayon" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" dependencies = [ - "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", - "syn 2.0.106", + "rayon", ] [[package]] -name = "parking" -version = "2.2.1" +name = "p3-maybe-rayon" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" + +[[package]] +name = "p3-mds" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +dependencies = [ + "p3-dft 0.3.0", + "p3-field 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", +] + +[[package]] +name = "p3-mds" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "p3-dft 0.4.1", + "p3-field 0.4.1", + "p3-symmetric 0.4.1", + "p3-util 0.4.1", + "rand 0.9.2", +] + +[[package]] +name = "p3-merkle-tree" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +dependencies = [ + "itertools 0.14.0", + "p3-commit", + "p3-field 0.3.0", + "p3-matrix 0.3.0", + "p3-maybe-rayon 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", + "serde", + "tracing", +] + +[[package]] +name = "p3-monty-31" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-dft 0.3.0", + "p3-field 0.3.0", + "p3-matrix 0.3.0", + "p3-maybe-rayon 0.3.0", + "p3-mds 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "paste", + "rand 0.9.2", + "serde", + "tracing", + "transpose", +] + +[[package]] +name = "p3-monty-31" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-dft 0.4.1", + "p3-field 0.4.1", + "p3-matrix 0.4.1", + "p3-maybe-rayon 0.4.1", + "p3-mds 0.4.1", + "p3-poseidon2 0.4.1", + "p3-symmetric 0.4.1", + "p3-util 0.4.1", + "paste", + "rand 0.9.2", + "serde", + "spin", + "tracing", + "transpose", +] + +[[package]] +name = "p3-poseidon2" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +dependencies = [ + "p3-field 0.3.0", + "p3-mds 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", +] + +[[package]] +name = "p3-poseidon2" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "p3-field 0.4.1", + "p3-mds 0.4.1", + "p3-symmetric 0.4.1", + "p3-util 0.4.1", + "rand 0.9.2", +] + +[[package]] +name = "p3-symmetric" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +dependencies = [ + "itertools 0.14.0", + "p3-field 0.3.0", + "serde", +] + +[[package]] +name = "p3-symmetric" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "itertools 0.14.0", + "p3-field 0.4.1", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.3.0" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +dependencies = [ + "rayon", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.4.1" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174ae1f7e528d4bb92b7b18ab295" +dependencies = [ + "serde", +] + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "parking" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" @@ -3525,14 +3965,47 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.3" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", "ucd-trie", ] +[[package]] +name = "pest_derive" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "pest_meta" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +dependencies = [ + "pest", + "sha2 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -3550,7 +4023,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3586,7 +4059,7 @@ dependencies = [ "hermit-abi", "pin-project-lite", "rustix", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -3614,9 +4087,25 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "poseidon_circuit" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "multilinear-toolkit", + "p3-koala-bear 0.3.0", + "p3-monty-31 0.3.0", + "p3-poseidon2 0.3.0", + "rand 0.9.2", + "sub_protocols", + "tracing", + "utils", + "whir-p3", +] [[package]] name = "potential_utf" @@ -3710,9 +4199,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -3737,19 +4226,18 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "proptest" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.4", - "lazy_static", + "bitflags 2.10.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -3803,7 +4291,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.6.1", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -3816,7 +4304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring", @@ -3824,7 +4312,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -3846,9 +4334,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -3883,7 +4371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.4", "serde", ] @@ -3904,7 +4392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.4", ] [[package]] @@ -3913,16 +4401,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "serde", ] @@ -3932,7 +4420,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.4", +] + +[[package]] +name = "rapidhash" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +dependencies = [ + "rustversion", ] [[package]] @@ -3968,40 +4465,68 @@ dependencies = [ "yasna", ] +[[package]] +name = "rec_aggregation" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "air", + "bincode", + "lean_compiler", + "lean_prover", + "lean_vm", + "lookup", + "multilinear-toolkit", + "p3-air", + "p3-challenger 0.3.0", + "p3-koala-bear 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", + "serde", + "serde_json", + "sub_protocols", + "tracing", + "utils", + "whir-p3", + "xmss", +] + [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -4011,9 +4536,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -4022,9 +4547,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "relative-path" @@ -4040,9 +4565,9 @@ checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" [[package]] name = "resolv-conf" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3789b30bd25ba102de4beabd95d21ac45b69b1be7d14522bab988c526d6799" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "rfc6979" @@ -4062,7 +4587,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -4103,7 +4628,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.114", "unicode-ident", ] @@ -4127,9 +4652,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.0" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" +checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -4200,22 +4725,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "once_cell", "ring", @@ -4227,9 +4752,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "web-time", "zeroize", @@ -4288,9 +4813,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "schemars" @@ -4306,9 +4831,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -4362,9 +4887,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -4372,42 +4897,42 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] name = "serde_utils" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#5bdc78763c8959ad689c79d51d7d59978460bb1e" +source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" dependencies = [ "const-hex", "generic-array", @@ -4422,19 +4947,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.1" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.4", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.0.4", - "serde", - "serde_derive", + "schemars 1.2.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -4442,14 +4966,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.1" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4458,26 +4982,13 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "itoa", "ryu", "serde", "unsafe-libyaml", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.9" @@ -4492,11 +5003,12 @@ dependencies = [ [[package]] name = "sha2" version = "0.10.9" -source = "git+https://github.com/grandinetech/universal-precompiles.git?tag=sha2-v0.10.9-up.1#dab12204de2e6a40f7a4c93b59347f60174c6953" +source = "git+https://github.com/grandinetech/universal-precompiles.git?tag=sha2-v0.10.9-up.2#7d57ea01cd5fe5f6458142ce6ac269cc44b425bd" dependencies = [ "cfg-if", "cpufeatures", "digest 0.10.7", + "ziskos", ] [[package]] @@ -4536,10 +5048,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -4630,7 +5143,7 @@ dependencies = [ [[package]] name = "ssz" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#5bdc78763c8959ad689c79d51d7d59978460bb1e" +source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" dependencies = [ "arithmetic", "bit_field", @@ -4653,7 +5166,7 @@ dependencies = [ "static_assertions", "std_ext", "tap", - "thiserror 2.0.16", + "thiserror 2.0.17", "triomphe", "try_from_iterator", "typenum", @@ -4662,7 +5175,7 @@ dependencies = [ [[package]] name = "ssz_derive" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#5bdc78763c8959ad689c79d51d7d59978460bb1e" +source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" dependencies = [ "darling", "easy-ext", @@ -4670,39 +5183,14 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "ssz_rs" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057291e5631f280978fa9c8009390663ca4613359fc1318e36a8c24c392f6d1f" -dependencies = [ - "bitvec", - "hex", - "num-bigint", - "serde", - "sha2 0.9.9", - "ssz_rs_derive", -] - -[[package]] -name = "ssz_rs_derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "syn 2.0.114", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -4713,7 +5201,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "std_ext" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#5bdc78763c8959ad689c79d51d7d59978460bb1e" +source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" dependencies = [ "easy-ext", "triomphe", @@ -4731,12 +5219,61 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "sub_protocols" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "derive_more", + "lookup", + "multilinear-toolkit", + "p3-util 0.3.0", + "tracing", + "utils", + "whir-p3", +] + [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "sumcheck" +version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +dependencies = [ + "backend", + "constraints-folder", + "fiat-shamir", + "p3-air", + "p3-field 0.3.0", + "p3-util 0.3.0", + "rayon", +] + [[package]] name = "syn" version = "1.0.109" @@ -4750,9 +5287,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -4779,7 +5316,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4788,7 +5325,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation", "system-configuration-sys", ] @@ -4817,15 +5354,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -4839,11 +5376,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -4854,18 +5391,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4944,9 +5481,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -4956,7 +5493,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.6.1", "tokio-macros", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -4967,14 +5504,14 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -4995,20 +5532,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "toml_datetime", "toml_parser", "winnow", @@ -5016,9 +5553,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] @@ -5031,9 +5568,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -5043,25 +5580,38 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", ] +[[package]] +name = "tracing-forest" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92bdb3c949c9e81b71f78ba782f956b896019d82cc2f31025d21e04adab4d695" +dependencies = [ + "ansi_term", + "smallvec", + "thiserror 2.0.17", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -5075,9 +5625,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -5103,9 +5653,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" dependencies = [ "serde", "stable_deref_trait", @@ -5120,7 +5670,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "try_from_iterator" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#5bdc78763c8959ad689c79d51d7d59978460bb1e" +source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" [[package]] name = "typenum" @@ -5166,9 +5716,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" @@ -5222,9 +5772,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -5244,13 +5794,30 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utils" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "multilinear-toolkit", + "p3-air", + "p3-challenger 0.3.0", + "p3-koala-bear 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "tracing", + "tracing-forest", + "tracing-subscriber", +] + [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", "wasm-bindgen", ] @@ -5263,7 +5830,6 @@ dependencies = [ "env-config", "fork-choice", "leansig", - "serde", "serde_yaml", "tracing", "typenum", @@ -5311,15 +5877,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -5331,9 +5888,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -5342,25 +5899,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5368,22 +5911,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -5398,12 +5941,61 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "whir-p3" +version = "0.1.0" +source = "git+https://github.com/TomWambsgans/whir-p3?branch=lean-multisig#04fb1c1f2e3bbd14e6e4aee32621656eb3f3949f" +dependencies = [ + "itertools 0.14.0", + "multilinear-toolkit", + "p3-baby-bear 0.3.0", + "p3-challenger 0.3.0", + "p3-commit", + "p3-dft 0.3.0", + "p3-field 0.3.0", + "p3-interpolation", + "p3-koala-bear 0.3.0", + "p3-matrix 0.3.0", + "p3-maybe-rayon 0.3.0", + "p3-merkle-tree", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", + "rayon", + "thiserror 2.0.17", + "tracing", + "tracing-forest", + "tracing-subscriber", +] + [[package]] name = "widestring" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.53.0" @@ -5426,44 +6018,44 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.62.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", "windows-link", - "windows-result 0.4.0", + "windows-result 0.4.1", "windows-strings", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" @@ -5476,18 +6068,18 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] @@ -5510,20 +6102,29 @@ 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-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.4", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] @@ -5561,9 +6162,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", "windows_aarch64_gnullvm 0.53.1", @@ -5716,9 +6317,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -5739,6 +6340,35 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "witness_generation" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "air", + "derive_more", + "lean_compiler", + "lean_vm", + "lookup", + "multilinear-toolkit", + "p3-air", + "p3-challenger 0.3.0", + "p3-koala-bear 0.3.0", + "p3-monty-31 0.3.0", + "p3-poseidon2 0.3.0", + "p3-symmetric 0.3.0", + "p3-util 0.3.0", + "pest", + "pest_derive", + "poseidon_circuit", + "rand 0.9.2", + "sub_protocols", + "tracing", + "utils", + "whir-p3", + "xmss", +] + [[package]] name = "writeable" version = "0.6.2" @@ -5779,7 +6409,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -5798,6 +6428,19 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "xmss" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +dependencies = [ + "multilinear-toolkit", + "p3-koala-bear 0.3.0", + "p3-util 0.3.0", + "rand 0.9.2", + "sha3", + "utils", +] + [[package]] name = "yamux" version = "0.12.1" @@ -5863,28 +6506,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "synstructure 0.13.2", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -5904,7 +6547,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "synstructure 0.13.2", ] @@ -5919,13 +6562,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -5958,5 +6601,27 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", +] + +[[package]] +name = "ziskos" +version = "0.13.0" +source = "git+https://github.com/0xPolygonHermez/zisk.git?tag=v0.13.0#ea1ed4c518992a170fc59ec19f1228eb4829a9e1" +dependencies = [ + "cfg-if", + "getrandom 0.2.17", + "lazy_static", + "lib-c", + "num-bigint", + "num-traits", + "rand 0.8.5", + "static_assertions", + "tiny-keccak", ] + +[[package]] +name = "zmij" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec" diff --git a/lean_client/Cargo.toml b/lean_client/Cargo.toml index 9e72c43..cb996ba 100644 --- a/lean_client/Cargo.toml +++ b/lean_client/Cargo.toml @@ -34,12 +34,13 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" snap = "1.1" ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop" } -ssz-derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop" } +ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop" } ssz-types = "0.3.0" tokio = { version = "1.0", features = ["full"] } tree-hash = "0.4.0" typenum = "1.19" sha2 = "0.10" +rand = "0.9" [workspace.dev-dependencies] rstest = "0.18.2" @@ -52,10 +53,9 @@ version = "0.1.0" edition = "2021" [features] -default = ["devnet2", "xmss-signing"] +default = ["xmss-signing", "containers/xmss-verify"] xmss-signing = ["validator/xmss-signing"] -devnet1 = ["containers/devnet1", "fork-choice/devnet1", "networking/devnet1", "validator/devnet1"] -devnet2 = ["containers/devnet2", "fork-choice/devnet2", "networking/devnet2", "validator/devnet2"] +xmss-verify = ["containers/xmss-verify"] [dependencies] chain = { path = "./chain" } diff --git a/lean_client/ENVIRONMENT_SELECTION.md b/lean_client/ENVIRONMENT_SELECTION.md deleted file mode 100644 index d906c9d..0000000 --- a/lean_client/ENVIRONMENT_SELECTION.md +++ /dev/null @@ -1,26 +0,0 @@ -### To select which devnet you want to compile - -#### Option A -- Change the default features in root `Cargo.toml`: -```toml -[features] -default = ["devnet1", "<...other features>"] # Change to "devnet2" if needed -devnet1 = [...] -devnet2 = [...] -``` - -#### Option B -- Use the `--no-default-features` flag and specify the desired devnet feature when building or running the project: -```bash -cargo build --no-default-features --features devnet1 # Change to devnet2 -``` - - -### Running tests for a specific devnet - -From root directory, use the following command: -```bash -cargo test -p --no-default-features --features devnet1 # Change to devnet2 -``` - -Use `` to specify the crate you want to test. \ No newline at end of file diff --git a/lean_client/Makefile b/lean_client/Makefile index 43c4534..d06597c 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -42,7 +42,7 @@ check-format: .PHONY: test test: - cargo test --workspace --features devnet2,xmss-signing --no-fail-fast + cargo test --workspace --all-features --no-fail-fast .PHONY: generate-test-vectors generate-test-vectors: diff --git a/lean_client/chain/src/config.rs b/lean_client/chain/src/config.rs index 6dd26fb..1d762de 100644 --- a/lean_client/chain/src/config.rs +++ b/lean_client/chain/src/config.rs @@ -11,6 +11,7 @@ impl BasisPoint { None } } + #[inline] pub fn get(&self) -> u64 { self.0 diff --git a/lean_client/containers/Cargo.toml b/lean_client/containers/Cargo.toml index 29e8ecd..0927f7e 100644 --- a/lean_client/containers/Cargo.toml +++ b/lean_client/containers/Cargo.toml @@ -4,10 +4,8 @@ version = "0.1.0" edition = "2021" [features] -xmss-verify = ["leansig"] +xmss-verify = [] default = [] -devnet1 = ["env-config/devnet1"] -devnet2 = ["env-config/devnet2"] [lib] name = "containers" @@ -15,15 +13,18 @@ path = "src/lib.rs" [dependencies] env-config = { path = "../env-config", default-features = false } -ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop", submodules = true } -ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop", submodules = false } +ssz = { workspace = true } +serde = { workspace = true } +ssz_derive = { workspace = true } typenum = "1" -serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.9" hex = "0.4.3" sha2 = "0.10" -leansig = { git = "https://github.com/leanEthereum/leanSig", branch = "main", optional = true } +leansig = { git = "https://github.com/leanEthereum/leanSig", branch = "main" } +lean-multisig = { git = "https://github.com/leanEthereum/leanMultisig", branch = "main" } +anyhow = "1.0.100" +alloy-primitives = "1.5.2" [dev-dependencies] rstest = "0.18" diff --git a/lean_client/containers/src/attestation.rs b/lean_client/containers/src/attestation.rs index 6779b0f..ba0596c 100644 --- a/lean_client/containers/src/attestation.rs +++ b/lean_client/containers/src/attestation.rs @@ -1,9 +1,15 @@ use crate::{Checkpoint, Slot, Uint64}; +use leansig::serialization::Serializable; use serde::{Deserialize, Serialize}; use ssz::BitList; use ssz::ByteVector; +use ssz::{SszHash, H256}; use ssz_derive::Ssz; -use typenum::{Prod, Sum, U100, U12, U31}; +use std::collections::HashSet; +use typenum::{Prod, Sum, U100, U1024, U12, U31}; + +// Type-level number for 1 MiB (1048576 = 1024 * 1024) +type U1048576 = Prod; pub type U3100 = Prod; @@ -22,19 +28,191 @@ pub type Attestations = ssz::PersistentList; pub type AggregatedAttestations = ssz::PersistentList; -#[cfg(feature = "devnet1")] -pub type AttestationSignatures = ssz::PersistentList; - -#[cfg(feature = "devnet2")] -pub type AttestationSignatures = ssz::PersistentList; +pub type AttestationSignatures = ssz::PersistentList; -#[cfg(feature = "devnet2")] +/// Legacy naive aggregated signature type (list of individual XMSS signatures). +/// Kept for backwards compatibility but no longer used in wire format. pub type NaiveAggregatedSignature = ssz::PersistentList; +/// Aggregated signature proof from lean-multisig zkVM. +/// +/// This is a variable-length byte list (up to 1 MiB) containing the serialized +/// proof bytes from `xmss_aggregate_signatures()`. The `#[ssz(transparent)]` +/// attribute makes this type serialize directly as a ByteList for SSZ wire format. +#[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] +#[ssz(transparent)] +pub struct MultisigAggregatedSignature( + /// The serialized zkVM proof bytes from lean-multisig aggregation. + #[serde(with = "crate::serde_helpers::byte_list")] + pub ssz::ByteList, +); + +impl MultisigAggregatedSignature { + /// Create a new MultisigAggregatedSignature from proof bytes. + pub fn new(proof: Vec) -> Result { + ssz::ByteList::try_from(proof) + .map(Self) + .map_err(|_| AggregationError::AggregationFailed) + } + + /// Get the proof bytes. + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } + + /// Check if the signature is empty (no proof). + pub fn is_empty(&self) -> bool { + self.0.as_bytes().is_empty() + } + + /// Aggregate individual XMSS signatures into a single proof. + /// + /// Uses lean-multisig zkVM to combine multiple signatures into a compact proof. + /// + /// # Arguments + /// * `public_keys` - Public keys of the signers + /// * `signatures` - Individual XMSS signatures to aggregate + /// * `message` - The 32-byte message that was signed (as 8 field elements) + /// * `epoch` - The epoch/slot in which signatures were created + /// + /// # Returns + /// Aggregated signature proof, or error if aggregation fails. + pub fn aggregate( + public_keys: &[lean_multisig::XmssPublicKey], + signatures: &[lean_multisig::XmssSignature], + message: [lean_multisig::F; 8], + epoch: u64, + ) -> Result { + if public_keys.is_empty() { + return Err(AggregationError::EmptyInput); + } + if public_keys.len() != signatures.len() { + return Err(AggregationError::MismatchedLengths); + } + + let proof_bytes = + lean_multisig::xmss_aggregate_signatures(public_keys, signatures, message, epoch) + .map_err(|_| AggregationError::AggregationFailed)?; + + Self::new(proof_bytes) + } + + /// Verify the aggregated signature proof against the given public keys and message. + /// + /// Uses lean-multisig zkVM to verify that the aggregated proof is valid + /// for all the given public keys signing the same message at the given epoch. + /// + /// # Returns + /// `Ok(())` if the proof is valid, `Err` with the proof error otherwise. + pub fn verify( + &self, + public_keys: &[lean_multisig::XmssPublicKey], + message: [lean_multisig::F; 8], + epoch: u64, + ) -> Result<(), AggregationError> { + lean_multisig::xmss_verify_aggregated_signatures( + public_keys, + message, + self.0.as_bytes(), + epoch, + ) + .map_err(|_| AggregationError::VerificationFailed) + } + + /// Verify the aggregated payload against validators and message. + /// + /// This is a convenience method that extracts public keys from validators + /// and converts the message bytes to the field element format expected by lean-multisig. + /// + /// # Arguments + /// * `validators` - Slice of validator references to extract public keys from + /// * `message` - 32-byte message (typically attestation data root) + /// * `epoch` - Epoch/slot for proof verification + /// + /// # Returns + /// `Ok(())` if verification succeeds, `Err` otherwise. + pub fn verify_aggregated_payload( + &self, + validators: &[&crate::validator::Validator], + message: &[u8; 32], + epoch: u64, + ) -> Result<(), AggregationError> { + // Extract public keys from validators + let mut public_keys = Vec::new(); + for validator in validators { + // Convert PublicKey to lean_multisig::XmssPublicKey + let lean_sig_pk = validator + .pubkey + .as_lean_sig() + .map_err(|_| AggregationError::VerificationFailed)?; + let pk_bytes = lean_sig_pk.to_bytes(); + // TODO: Implement proper conversion from PublicKey bytes to lean_multisig::XmssPublicKey + // Once lean-multisig API is clarified, convert pk_bytes to XmssPublicKey + todo!("Convert PublicKey to lean_multisig::XmssPublicKey and implement message field conversion"); + } + + // Convert 32-byte message to 8 field elements + // TODO: Implement proper conversion from 32 bytes to 8 field elements + let message_fields = todo!("Convert 32-byte message to [lean_multisig::F; 8]"); + + // Call verify with extracted keys and converted message + self.verify(&public_keys, message_fields, epoch) + } +} + +/// Error types for signature aggregation operations. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AggregationError { + /// No signatures provided for aggregation. + EmptyInput, + /// Public keys and signatures arrays have different lengths. + MismatchedLengths, + /// Aggregation failed in lean-multisig. + AggregationFailed, + /// Verification of aggregated proof failed. + VerificationFailed, +} + +/// Aggregated signature proof with participant tracking. +/// +/// This type combines the participant bitfield with the proof bytes, +/// matches Python's `AggregatedSignatureProof` container structure. +/// Used in `aggregated_payloads` to track which validators are covered by each proof. +#[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AggregatedSignatureProof { + /// Bitfield indicating which validators' signatures are included. + pub participants: AggregationBits, + /// The raw aggregated proof bytes from lean-multisig. + pub proof_data: MultisigAggregatedSignature, +} + +impl AggregatedSignatureProof { + /// Create a new AggregatedSignatureProof. + pub fn new(participants: AggregationBits, proof_data: MultisigAggregatedSignature) -> Self { + Self { + participants, + proof_data, + } + } + + pub fn from_aggregation(participant_ids: &[u64], proof: MultisigAggregatedSignature) -> Self { + Self { + participants: AggregationBits::from_validator_indices(participant_ids), + proof_data: proof, + } + } + + /// Get the validator indices covered by this proof. + pub fn get_participant_indices(&self) -> Vec { + self.participants.to_validator_indices() + } +} + /// Bitlist representing validator participation in an attestation. /// Limit is VALIDATOR_REGISTRY_LIMIT (4096). #[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] -pub struct AggregationBits(pub BitList); +pub struct AggregationBits(#[serde(with = "crate::serde_helpers::bitlist")] pub BitList); impl AggregationBits { pub const LIMIT: u64 = 4096; @@ -98,6 +276,34 @@ pub struct AttestationData { pub source: Checkpoint, } +impl AttestationData { + /// Compute the data root bytes for signature lookup. + /// This is the hash tree root of the attestation data. + pub fn data_root_bytes(&self) -> crate::Bytes32 { + crate::Bytes32(ssz::SszHash::hash_tree_root(self)) + } +} + +/// Key for looking up individual validator signatures. +/// Used to index signature caches by (validator, attestation_data_root) pairs. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SignatureKey { + /// The validator who produced the signature. + pub validator_id: u64, + /// The hash of the signed attestation data. + pub data_root: crate::Bytes32, +} + +impl SignatureKey { + /// Create a new signature key. + pub fn new(validator_id: u64, data_root: crate::Bytes32) -> Self { + Self { + validator_id, + data_root, + } + } +} + /// Validator specific attestation wrapping shared attestation data. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -111,17 +317,14 @@ pub struct Attestation { /// Validator attestation bundled with its signature. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct SignedAttestation { - #[cfg(feature = "devnet2")] pub validator_id: u64, - #[cfg(feature = "devnet2")] pub message: AttestationData, - #[cfg(feature = "devnet1")] - pub message: Attestation, pub signature: Signature, } /// Aggregated attestation consisting of participation bits and message. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct AggregatedAttestation { /// Bitfield indicating which validators participated in the aggregation. pub aggregation_bits: AggregationBits, @@ -158,16 +361,16 @@ impl AggregatedAttestation { .collect() } - pub fn to_plain(&self) -> Vec { - let validator_indices = self.aggregation_bits.to_validator_indices(); - - validator_indices - .into_iter() - .map(|validator_id| Attestation { - validator_id: Uint64(validator_id), - data: self.data.clone(), - }) - .collect() + /// Returns true if the provided list contains duplicate AttestationData. + pub fn has_duplicate_data(attestations: &AggregatedAttestations) -> bool { + let mut seen: HashSet = HashSet::new(); + for attestation in attestations { + let root = attestation.data.hash_tree_root(); + if !seen.insert(root) { + return true; + } + } + false } } diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index 0acf1b2..52d6d59 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -1,13 +1,10 @@ -use crate::{Attestation, Attestations, Bytes32, Signature, Slot, State, ValidatorIndex}; +use crate::{ + Attestation, Bytes32, MultisigAggregatedSignature, Signature, Slot, State, ValidatorIndex, +}; use serde::{Deserialize, Serialize}; use ssz_derive::Ssz; -#[cfg(feature = "xmss-verify")] -use leansig::signature::generalized_xmss::instantiations_poseidon::lifetime_2_to_the_20::target_sum::SIGTargetSumLifetime20W2NoOff; -use ssz::{PersistentList, SszHash}; -use typenum::U4096; use crate::attestation::{AggregatedAttestations, AttestationSignatures}; -use crate::validator::BlsPublicKey; /// The body of a block, containing payload data. /// @@ -15,11 +12,8 @@ use crate::validator::BlsPublicKey; /// separately in BlockSignatures to match the spec architecture. #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct BlockBody { - #[cfg(feature = "devnet2")] + #[serde(with = "crate::serde_helpers::aggregated_attestations")] pub attestations: AggregatedAttestations, - #[cfg(feature = "devnet1")] - #[serde(with = "crate::serde_helpers")] - pub attestations: Attestations, } #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] @@ -53,8 +47,11 @@ pub struct BlockWithAttestation { } #[derive(Debug, PartialEq, Eq, Clone, Serialize, Ssz, Deserialize, Default)] +#[serde(rename_all = "camelCase")] pub struct BlockSignatures { + #[serde(with = "crate::serde_helpers::attestation_signatures")] pub attestation_signatures: AttestationSignatures, + #[serde(with = "crate::serde_helpers::signature")] pub proposer_signature: Signature, } @@ -67,10 +64,6 @@ pub struct SignedBlockWithAttestation { /// Aggregated signature payload for the block. /// /// Signatures remain in attestation order followed by the proposer signature. - #[cfg(feature = "devnet1")] - #[serde(with = "crate::serde_helpers::block_signatures")] - pub signature: PersistentList, - #[cfg(feature = "devnet2")] pub signature: BlockSignatures, } @@ -128,95 +121,9 @@ impl SignedBlockWithAttestation { /// /// - Spec: /// - XMSS Library: - #[cfg(feature = "devnet1")] - pub fn verify_signatures(&self, parent_state: State) -> bool { - // Unpack the signed block components - let block = &self.message.block; - let signatures = &self.signature; - - // Combine all attestations that need verification - // - // This creates a single list containing both: - // 1. Block body attestations (from other validators) - // 2. Proposer attestation (from the block producer) - let mut all_attestations: Vec = Vec::new(); - - // Collect block body attestations - let mut i: u64 = 0; - loop { - match block.body.attestations.get(i) { - Ok(a) => all_attestations.push(a.clone()), - Err(_) => break, - } - i += 1; - } - - // Append proposer attestation - all_attestations.push(self.message.proposer_attestation.clone()); - - // Collect signatures into a Vec - let mut signatures_vec: Vec = Vec::new(); - let mut j: u64 = 0; - loop { - match signatures.get(j) { - Ok(s) => signatures_vec.push(s.clone()), - Err(_) => break, - } - j += 1; - } - - // Verify signature count matches attestation count - // - // Each attestation must have exactly one corresponding signature. - // - // The ordering must be preserved: - // 1. Block body attestations, - // 2. The proposer attestation. - assert_eq!( - signatures_vec.len(), - all_attestations.len(), - "Number of signatures does not match number of attestations" - ); - - let validators = &parent_state.validators; - let num_validators = validators.len_u64(); - - // Verify each attestation signature - for (attestation, signature) in all_attestations.iter().zip(signatures_vec.iter()) { - // Ensure validator exists in the active set - assert!( - attestation.validator_id.0 < num_validators, - "Validator index out of range" - ); - - let validator = validators - .get(attestation.validator_id.0) - .expect("validator must exist"); - - // Verify the XMSS signature - // - // This cryptographically proves that: - // - The validator possesses the secret key for their public key - // - The attestation has not been tampered with - // - The signature was created at the correct epoch (slot) - - let message_bytes: [u8; 32] = hash_tree_root(attestation).0.into(); - - assert!( - verify_xmss_signature( - validator.pubkey.0.as_bytes(), - attestation.data.slot, - &message_bytes, - &signature, - ), - "Attestation signature verification failed" - ); - } - - true - } - - #[cfg(feature = "devnet2")] + /// Verifies all attestation signatures using lean-multisig aggregated proofs. + /// Each attestation has a single `MultisigAggregatedSignature` proof that covers + /// all participating validators. pub fn verify_signatures(&self, parent_state: State) -> bool { // Unpack the signed block components let block = &self.message.block; @@ -228,14 +135,14 @@ impl SignedBlockWithAttestation { assert_eq!( aggregated_attestations.len_u64(), attestation_signatures.len_u64(), - "Number of signatures does not match number of attestations" + "Attestation signature groups must align with block body attestations" ); let validators = &parent_state.validators; let num_validators = validators.len_u64(); - // Verify each attestation signature - for (aggregated_attestation, aggregated_signature) in (&aggregated_attestations) + // Verify each aggregated attestation's zkVM proof + for (aggregated_attestation, _aggregated_signature_proof) in (&aggregated_attestations) .into_iter() .zip((&attestation_signatures).into_iter()) { @@ -243,67 +150,57 @@ impl SignedBlockWithAttestation { .aggregation_bits .to_validator_indices(); - assert_eq!( - aggregated_signature.len_u64(), - validator_ids.len() as u64, - "Aggregated attestation signature count mismatch" - ); - - let attestation_root = aggregated_attestation.data.hash_tree_root(); - - // Loop through zipped validator IDs and their corresponding signatures - // Verify each individual signature within the aggregated attestation - for (validator_id, signature) in - validator_ids.iter().zip(aggregated_signature.into_iter()) - { - // Ensure validator exists in the active set + // Ensure all validators exist in the active set + for validator_id in &validator_ids { assert!( *validator_id < num_validators, "Validator index out of range" ); - - let validator = validators.get(*validator_id).expect("validator must exist"); - - // Get the actual payload root for the attestation data - let attestation_root: [u8; 32] = - hash_tree_root(&aggregated_attestation.data).0.into(); - - // Verify the XMSS signature - assert!( - verify_xmss_signature( - validator.pubkey.0.as_bytes(), - aggregated_attestation.data.slot, - &attestation_root, - signature, - ), - "Attestation signature verification failed" - ); } - // Verify the proposer attestation signature - let proposer_attestation = self.message.proposer_attestation.clone(); - let proposer_signature = signatures.proposer_signature; + // let attestation_data_root: [u8; 32] = + // hash_tree_root(&aggregated_attestation.data).0.into(); - assert!( - proposer_attestation.validator_id.0 < num_validators, - "Proposer index out of range" - ); + // Verify the lean-multisig aggregated proof for this attestation + // + // The proof verifies that all validators in aggregation_bits signed + // the same attestation_data_root at the given epoch (slot). + // TODO + // aggregated_signature_proof + // .verify_aggregated_payload( + // &validator_ids + // .iter() + // .map(|vid| validators.get(*vid).expect("validator must exist")) + // .collect::>(), + // &attestation_data_root, + // aggregated_attestation.data.slot.0, + // ) + // .expect("Attestation aggregated signature verification failed"); + } - let proposer = validators - .get(proposer_attestation.validator_id.0) - .expect("proposer must exist"); + // Verify the proposer attestation signature (outside the attestation loop) + let proposer_attestation = &self.message.proposer_attestation; + let proposer_signature = &signatures.proposer_signature; - let proposer_root: [u8; 32] = hash_tree_root(&proposer_attestation).0.into(); - assert!( - verify_xmss_signature( - proposer.pubkey.0.as_bytes(), - proposer_attestation.data.slot, - &proposer_root, - &proposer_signature, - ), - "Proposer attestation signature verification failed" - ); - } + assert!( + proposer_attestation.validator_id.0 < num_validators, + "Proposer index out of range" + ); + + let proposer = validators + .get(proposer_attestation.validator_id.0) + .expect("proposer must exist"); + + let proposer_root: [u8; 32] = hash_tree_root(&proposer_attestation.data).0.into(); + assert!( + verify_xmss_signature( + proposer.pubkey, + proposer_attestation.data.slot, + &proposer_root, + proposer_signature, + ), + "Proposer attestation signature verification failed" + ); true } @@ -311,34 +208,22 @@ impl SignedBlockWithAttestation { #[cfg(feature = "xmss-verify")] pub fn verify_xmss_signature( - pubkey_bytes: &[u8], + public_key: crate::public_key::PublicKey, slot: Slot, message_bytes: &[u8; 32], signature: &Signature, ) -> bool { - use leansig::serialization::Serializable; - use leansig::signature::SignatureScheme; - let epoch = slot.0 as u32; + let signature = crate::signature::Signature::from(signature.as_bytes()); - type PubKey = ::PublicKey; - let pubkey = match PubKey::from_bytes(pubkey_bytes) { - Ok(pk) => pk, - Err(_) => return false, - }; - - type Sig = ::Signature; - let sig = match Sig::from_bytes(signature.as_bytes()) { - Ok(s) => s, - Err(_) => return false, - }; - - SIGTargetSumLifetime20W2NoOff::verify(&pubkey, epoch, message_bytes, &sig) + signature + .verify(&public_key, epoch, message_bytes) + .unwrap_or_else(|_| false) } #[cfg(not(feature = "xmss-verify"))] pub fn verify_xmss_signature( - _pubkey_bytes: &[u8], + _public_key: crate::public_key::PublicKey, _slot: Slot, _message_bytes: &[u8; 32], _signature: &Signature, diff --git a/lean_client/containers/src/lib.rs b/lean_client/containers/src/lib.rs index f0590ca..0125a08 100644 --- a/lean_client/containers/src/lib.rs +++ b/lean_client/containers/src/lib.rs @@ -2,7 +2,9 @@ pub mod attestation; pub mod block; pub mod checkpoint; pub mod config; +pub mod public_key; pub mod serde_helpers; +pub mod signature; pub mod slot; pub mod state; pub mod status; @@ -11,8 +13,10 @@ pub mod validator; pub use attestation::{ AggregatedAttestation, AggregatedSignatures, AggregationBits, Attestation, AttestationData, - Attestations, Signature, SignedAggregatedAttestation, SignedAttestation, + Attestations, Signature, SignatureKey, SignedAggregatedAttestation, SignedAttestation, }; + +pub use attestation::{AggregatedSignatureProof, MultisigAggregatedSignature}; pub use block::{ Block, BlockBody, BlockHeader, BlockWithAttestation, SignedBlock, SignedBlockWithAttestation, }; diff --git a/lean_client/containers/src/public_key.rs b/lean_client/containers/src/public_key.rs new file mode 100644 index 0000000..114b17c --- /dev/null +++ b/lean_client/containers/src/public_key.rs @@ -0,0 +1,207 @@ +use alloy_primitives::{hex::{self, ToHexExt}}; +use anyhow::{anyhow}; +use leansig::{serialization::Serializable, signature::SignatureScheme}; +use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8; +use serde::{Deserialize, Deserializer, Serialize}; +use ssz::{SszSize, SszRead, SszWrite, SszHash, Size, WriteError, ReadError, H256}; + +const PUBLIC_KEY_SIZE: usize = 52; +pub type LeanSigPublicKey = + ::PublicKey; + +// This is a wrapper class for storing public keys, implementation based on Ream client +#[derive(Debug, PartialEq, Clone, Eq, Hash, Copy)] +pub struct PublicKey { + pub inner: [u8; PUBLIC_KEY_SIZE], +} + +impl From<&[u8]> for PublicKey { + fn from(value: &[u8]) -> Self { + // Handle potential length panics or ensure slice is correct size + let mut inner = [0u8; PUBLIC_KEY_SIZE]; + let len = value.len().min(PUBLIC_KEY_SIZE); + inner[..len].copy_from_slice(&value[..len]); + Self { inner } + } +} + +impl Default for PublicKey { + fn default() -> Self { + Self { + inner: [0u8; PUBLIC_KEY_SIZE], + } + } +} + +impl SszSize for PublicKey { + const SIZE: Size = Size::Fixed { + size: PUBLIC_KEY_SIZE, + }; +} + +// 2. Define how to write (Serialize) +impl SszWrite for PublicKey { + fn write_fixed(&self, _bytes: &mut [u8]) { + panic!("SszWrite::write_fixed must be implemented for fixed-size types"); + } + + fn write_variable(&self, _bytes: &mut Vec) -> Result<(), WriteError> { + panic!("SszWrite::write_variable must be implemented for variable-size types"); + } + + fn to_ssz(&self) -> Result, WriteError> { + match Self::SIZE { + Size::Fixed { size } => { + let mut bytes = vec![0; size]; + self.write_fixed(bytes.as_mut_slice()); + Ok(bytes) + } + Size::Variable { minimum_size } => { + let mut bytes = Vec::with_capacity(minimum_size); + self.write_variable(&mut bytes)?; + Ok(bytes) + } + } + } +} + +impl SszRead for PublicKey { + fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { + // For a fixed-size struct, we must ensure we have exactly + // the number of bytes required by our SszSize implementation. + if bytes.len() != PUBLIC_KEY_SIZE { + return Err(ReadError::FixedSizeMismatch { + expected: PUBLIC_KEY_SIZE, + actual: bytes.len(), + }); + } + + let mut inner = [0u8; PUBLIC_KEY_SIZE]; + inner.copy_from_slice(bytes); + + Ok(Self { inner }) + } + fn from_ssz(context: &C, bytes: impl AsRef<[u8]>) -> Result { + let bytes_ref = bytes.as_ref(); + + // SSZ fixed-size validation + if bytes_ref.len() != PUBLIC_KEY_SIZE { + return Err(ReadError::FixedSizeMismatch { + expected: PUBLIC_KEY_SIZE, + actual: bytes_ref.len(), + }); + } + + Self::from_ssz_unchecked(context, bytes_ref) + } +} + +impl SszHash for PublicKey { + type PackingFactor = typenum::U1; + + fn hash_tree_root(&self) -> H256 { + // Simple implementation: hash the inner bytes directly + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(&self.inner); + let result = hasher.finalize(); + H256::from_slice(&result) + } +} + +impl PublicKey { + pub fn new(inner: [u8; PUBLIC_KEY_SIZE]) -> Self { + Self { inner } + } + + pub fn from_lean_sig(public_key: LeanSigPublicKey) -> Result { + let bytes = public_key.to_bytes(); + // Ensure we fit into 52 bytes + if bytes.len() != PUBLIC_KEY_SIZE { + return Err(anyhow!( + "LeanSigPublicKey length mismatch: expected 52, got {}", + bytes.len() + )); + } + let mut inner = [0u8; PUBLIC_KEY_SIZE]; + inner.copy_from_slice(&bytes); + Ok(Self { inner }) + } + + pub fn as_lean_sig(&self) -> anyhow::Result { + LeanSigPublicKey::from_bytes(&self.inner) + .map_err(|err| anyhow!("Failed to decode LeanSigPublicKey from SSZ: {err:?}")) + } + + pub fn from_hex>(s: S) -> anyhow::Result { + let s = s.as_ref(); + + // Allow optional 0x prefix + let s = s.strip_prefix("0x").unwrap_or(s); + + let bytes = hex::decode(s).map_err(|e| anyhow!("Invalid hex public key: {e}"))?; + + if bytes.len() != 52 { + return Err(anyhow!( + "PublicKey hex length mismatch: expected 52 bytes, got {}", + bytes.len() + )); + } + + // Validate structure via LeanSig + let lean_pk = LeanSigPublicKey::from_bytes(&bytes) + .map_err(|e| anyhow!("Invalid XMSS public key encoding: {e:?}"))?; + + Self::from_lean_sig(lean_pk) + } + + pub fn debug_roundtrip(&self) -> anyhow::Result<()> { + let pk = self.as_lean_sig()?; + let re = pk.to_bytes(); + + anyhow::ensure!( + re.as_slice() == self.inner.as_slice(), + "PublicKey roundtrip mismatch: decoded->encoded bytes differ" + ); + + Ok(()) + } + + pub fn fingerprint_hex(&self) -> String { + use alloy_primitives::hex::ToHexExt; + let take = self.inner.len().min(12); + format!("0x{}", ToHexExt::encode_hex(&self.inner[..take].iter())) + } +} + +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!( + "0x{}", + self.as_lean_sig() + .map_err(serde::ser::Error::custom)? + .to_bytes() + .encode_hex() + )) + } +} + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let result: String = Deserialize::deserialize(deserializer)?; + let result = hex::decode(&result).map_err(serde::de::Error::custom)?; + + Self::from_lean_sig( + LeanSigPublicKey::from_bytes(&result) + .map_err(|err| anyhow!("Convert to error, with error trait implemented {err:?}")) + .map_err(serde::de::Error::custom)?, + ) + .map_err(serde::de::Error::custom) + } +} diff --git a/lean_client/containers/src/serde_helpers.rs b/lean_client/containers/src/serde_helpers.rs index 01604e5..3f3aa86 100644 --- a/lean_client/containers/src/serde_helpers.rs +++ b/lean_client/containers/src/serde_helpers.rs @@ -120,7 +120,7 @@ pub mod signature { siblings: DataWrapper>>>, } - pub fn deserialize_single<'de, D>(deserializer: D) -> Result + pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { @@ -130,7 +130,7 @@ pub mod signature { let value = Value::deserialize(deserializer)?; // Check if it's a hex string (normal format) - if let Value::String(hex_str) = &value { + if let Value::String(hex_str) = value { let hex_str = hex_str.trim_start_matches("0x"); let bytes = hex::decode(hex_str) .map_err(|e| D::Error::custom(format!("Invalid hex string: {}", e)))?; @@ -140,36 +140,96 @@ pub mod signature { } // Otherwise, parse as structured XMSS signature - let xmss_sig: XmssSignature = serde_json::from_value(value) + let xmss_sig: XmssSignature = serde_json::from_value(value.clone()) .map_err(|e| D::Error::custom(format!("Failed to parse XMSS signature: {}", e)))?; - // Serialize the XMSS signature to bytes - // Format: siblings (variable length) + rho (28 bytes) + hashes (variable length) - let mut bytes = Vec::new(); + println!( + "Parsed XMSS Signature | siblings: {:?}", + xmss_sig.path.siblings.data.len() + ); + println!("Parsed XMSS Signature | rho: {:?}", xmss_sig.rho.data.len()); + println!( + "Parsed XMSS Signature | hashes: {:?}", + xmss_sig.hashes.data.len() + ); + + // --- STEP 1: PREPARE DATA BUFFERS --- + + // 1. Serialize Rho (Fixed length) + // RAND_LEN_FE = 7, assuming u32 elements -> 28 bytes + let mut rho_bytes = Vec::new(); + for val in &xmss_sig.rho.data { + rho_bytes.extend_from_slice(&val.to_le_bytes()); + } + let rho_len = rho_bytes.len(); // Should be 28 (7 * 4) - // Write siblings + // 2. Serialize Path/Siblings (Variable length) + let mut path_bytes = Vec::new(); + // Prepend 4 bytes (containing 4) as an offset which would come with real SSZ serialization + let inner_offset: u32 = 4; + path_bytes.extend_from_slice(&inner_offset.to_le_bytes()); // [04 00 00 00] for sibling in &xmss_sig.path.siblings.data { for val in &sibling.data { - bytes.extend_from_slice(&val.to_le_bytes()); + path_bytes.extend_from_slice(&val.to_le_bytes()); } } - // Write rho (7 u32s = 28 bytes) - for val in &xmss_sig.rho.data { - bytes.extend_from_slice(&val.to_le_bytes()); - } - - // Write hashes + // 3. Serialize Hashes (Variable length) + let mut hashes_bytes = Vec::new(); for hash in &xmss_sig.hashes.data { for val in &hash.data { - bytes.extend_from_slice(&val.to_le_bytes()); + hashes_bytes.extend_from_slice(&val.to_le_bytes()); } } - // Pad or truncate to 3112 bytes - bytes.resize(3112, 0); + // --- STEP 2: CALCULATE OFFSETS --- + + // The fixed part contains: + // 1. Path Offset (4 bytes) + // 2. Rho Data (rho_len bytes) + // 3. Hashes Offset (4 bytes) + let fixed_part_size = 4 + rho_len + 4; + + // Offset to 'path' starts immediately after the fixed part + let offset_path = fixed_part_size as u32; + + // Offset to 'hashes' starts after 'path' data + let offset_hashes = offset_path + (path_bytes.len() as u32); + + // --- STEP 3: CONSTRUCT FINAL SSZ BYTES --- + + // Print all offsets and lengths for debugging + println!( + "SSZ Offsets | offset_path: {} | offset_hashes: {}", + offset_path, offset_hashes + ); + println!( + "SSZ Lengths | rho_len: {} | path_len: {} | hashes_len: {}", + rho_len, + path_bytes.len(), + hashes_bytes.len() + ); - Signature::try_from(bytes.as_slice()) + let mut ssz_bytes = Vec::new(); + + // 1. Write Offset to Path (u32, Little Endian) + ssz_bytes.extend_from_slice(&offset_path.to_le_bytes()); + + // 2. Write Rho Data (Fixed) + ssz_bytes.extend_from_slice(&rho_bytes); + + // 3. Write Offset to Hashes (u32, Little Endian) + ssz_bytes.extend_from_slice(&offset_hashes.to_le_bytes()); + + // 4. Write Path Data (Variable) + ssz_bytes.extend_from_slice(&path_bytes); + + // 5. Write Hashes Data (Variable) + ssz_bytes.extend_from_slice(&hashes_bytes); + + println!("Total SSZ Bytes Length: {}", ssz_bytes.len()); + + Signature::try_from(ssz_bytes.as_slice()) .map_err(|_| D::Error::custom("Failed to create signature")) } @@ -183,139 +243,142 @@ pub mod signature { } } -/// Custom deserializer for BlockSignatures that handles the {"data": [sig, ...]} format +/// Custom deserializer for AttestationSignatures that handles the {"data": [sig, ...]} format /// where each signature can be either hex string or structured XMSS format -pub mod block_signatures { +pub mod attestation_signatures { use super::*; - use crate::block::BlockSignatures; - use crate::Signature; - use serde_json::Value; + use crate::attestation::AttestationSignatures; + use crate::AggregatedSignatureProof; + use serde::de::Error; use ssz::PersistentList; use typenum::U4096; + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let outer: DataWrapper> = + DataWrapper::deserialize(deserializer)?; - /// Structured XMSS signature format from test vectors - #[derive(Deserialize, Clone)] - struct XmssSignature { - path: XmssPath, - rho: DataWrapper>, - hashes: DataWrapper>>>, - } - - #[derive(Deserialize, Clone)] - struct XmssPath { - siblings: DataWrapper>>>, - } - - fn parse_single_signature(value: &Value) -> Result { - // Check if it's a hex string (normal format) - if let Value::String(hex_str) = value { - let hex_str = hex_str.trim_start_matches("0x"); - let bytes = hex::decode(hex_str).map_err(|e| format!("Invalid hex string: {}", e))?; - - return Signature::try_from(bytes.as_slice()) - .map_err(|_| "Invalid signature length".to_string()); - } - - // Otherwise, parse as structured XMSS signature - let xmss_sig: XmssSignature = serde_json::from_value(value.clone()) - .map_err(|e| format!("Failed to parse XMSS signature: {}", e))?; - - // Serialize the XMSS signature to bytes - // Format: siblings (variable length) + rho (28 bytes) + hashes (variable length) - let mut bytes = Vec::new(); - - // Write siblings - for sibling in &xmss_sig.path.siblings.data { - for val in &sibling.data { - bytes.extend_from_slice(&val.to_le_bytes()); - } - } - - // Write rho (7 u32s = 28 bytes) - for val in &xmss_sig.rho.data { - bytes.extend_from_slice(&val.to_le_bytes()); - } + let mut out: PersistentList = PersistentList::default(); - // Write hashes - for hash in &xmss_sig.hashes.data { - for val in &hash.data { - bytes.extend_from_slice(&val.to_le_bytes()); - } + for aggregated_proof in outer.data.into_iter() { + out.push(aggregated_proof).map_err(|e| { + D::Error::custom(format!( + "AttestationSignatures push aggregated entry failed: {e:?}" + )) + })?; } - // Pad or truncate to 3112 bytes - bytes.resize(3112, 0); + Ok(out) + } - Signature::try_from(bytes.as_slice()).map_err(|_| "Failed to create signature".to_string()) + pub fn serialize(_value: &AttestationSignatures, _serializer: S) -> Result + where + S: Serializer, + { + // let mut inner: Vec = Vec::new(); + // + // // inner.push(format!("0x{}", hex::encode(sig.as_bytes()))); + // for sig in value.into_iter() { + // inner.push(format!("0x{}", hex::encode(sig.as_bytes()))); + // } + // + // DataWrapper { data: inner }.serialize(serializer) + // TODO: implement serialization + Err(serde::ser::Error::custom( + "AttestationSignatures serialization not implemented for devnet2", + )) } +} - #[cfg(feature = "devnet1")] - pub fn deserialize<'de, D>( - deserializer: D, - ) -> Result, D::Error> +/// Serde helper for ssz::ByteList - serializes as hex string +pub mod byte_list { + use super::*; + use ssz::ByteList; + use typenum::Unsigned; + + pub fn deserialize<'de, D, N>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, + N: Unsigned, { use serde::de::Error; - // Parse the {"data": [...]} wrapper - let wrapper: DataWrapper> = DataWrapper::deserialize(deserializer)?; + println!("Deserializing ByteList..."); - let mut signatures = PersistentList::default(); + // First, try to parse as a JSON value to inspect the structure + // let value = Value::deserialize(deserializer)?; + let wrapper = DataWrapper::::deserialize(deserializer)?; - for (idx, sig_value) in wrapper.data.into_iter().enumerate() { - let sig = parse_single_signature(&sig_value) - .map_err(|e| D::Error::custom(format!("Signature {}: {}", idx, e)))?; - signatures - .push(sig) - .map_err(|e| D::Error::custom(format!("Signature {} push failed: {:?}", idx, e)))?; - } + println!("Wrapper data length: {}", wrapper.data.len()); + + // Check if it's a hex string (normal format) + match wrapper.data { + hex_str => { + let hex_str = hex_str.trim_start_matches("0x"); + + if hex_str.is_empty() { + return Ok(ByteList::default()); + } - Ok(signatures) + let bytes = hex::decode(hex_str) + .map_err(|e| D::Error::custom(format!("Invalid hex string: {}", e)))?; + + println!("Decoded ByteList bytes length: {}", bytes.len()); + + return ByteList::try_from(bytes) + .map_err(|_| D::Error::custom("ByteList exceeds maximum length")); + } + } } - #[cfg(feature = "devnet2")] - pub fn deserialize<'de, D>(_: D) -> Result + pub fn serialize(value: &ByteList, serializer: S) -> Result where - D: Deserializer<'de>, + S: Serializer, + N: Unsigned, { - Err(serde::de::Error::custom( - "BlockSignatures deserialization not implemented for devnet2", - )) + let hex_str = format!("0x{}", hex::encode(value.as_bytes())); + hex_str.serialize(serializer) } +} - #[cfg(feature = "devnet1")] - pub fn serialize( - value: &PersistentList, - serializer: S, - ) -> Result +/// Custom deserializer for AggregatedAttestations that handles the {"data": [sig, ...]} format +/// where each signature can be either hex string or structured XMSS format +pub mod aggregated_attestations { + use super::*; + use crate::attestation::AggregatedAttestations; + use crate::AggregatedAttestation; + use serde::de::Error; + use ssz::PersistentList; + use typenum::U4096; + + pub fn deserialize<'de, D>(deserializer: D) -> Result where - S: Serializer, + D: Deserializer<'de>, { - // Collect all signatures as hex strings - let mut sigs: Vec = Vec::new(); - let mut i = 0u64; - loop { - match value.get(i) { - Ok(sig) => { - sigs.push(format!("0x{}", hex::encode(sig.as_bytes()))); - i += 1; - } - Err(_) => break, - } + let outer: DataWrapper> = + DataWrapper::deserialize(deserializer)?; + + let mut out: PersistentList = PersistentList::default(); + + for aggregated_attestations in outer.data.into_iter() { + out.push(aggregated_attestations).map_err(|e| { + D::Error::custom(format!( + "AggregatedAttestations push aggregated entry failed: {e:?}" + )) + })?; } - let wrapper = DataWrapper { data: sigs }; - wrapper.serialize(serializer) + Ok(out) } - #[cfg(feature = "devnet2")] - pub fn serialize(_value: &BlockSignatures, _serializer: S) -> Result + pub fn serialize(_value: &AggregatedAttestations, _serializer: S) -> Result where S: Serializer, { + // TODO: implement serialization Err(serde::ser::Error::custom( - "BlockSignatures serialization not implemented for devnet2", + "AttestationSignatures serialization not implemented for devnet2", )) } } diff --git a/lean_client/containers/src/signature.rs b/lean_client/containers/src/signature.rs new file mode 100644 index 0000000..ab39873 --- /dev/null +++ b/lean_client/containers/src/signature.rs @@ -0,0 +1,121 @@ +use alloy_primitives::hex::ToHexExt; +use anyhow::anyhow; +use leansig::{MESSAGE_LENGTH, serialization::Serializable, signature::SignatureScheme}; +use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8; +use serde::{Deserialize, Deserializer, Serialize}; +use crate::public_key::{PublicKey}; + +const SIGNATURE_SIZE: usize = 3112; + +type LeanSigSignature = ::Signature; + +/// Wrapper around a fixed-size serialized hash-based signature. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Signature { + pub inner: [u8; SIGNATURE_SIZE], +} + +impl From<&[u8]> for Signature { + fn from(value: &[u8]) -> Self { + // Handle potential length panics or ensure slice is correct size + let mut inner = [0u8; SIGNATURE_SIZE]; + let len = value.len().min(SIGNATURE_SIZE); + inner[..len].copy_from_slice(&value[..len]); + Self { inner } + } +} + +impl Signature { + pub fn new(inner: [u8; SIGNATURE_SIZE]) -> Self { + Self { inner } + } + + pub fn from_lean_sig(signature: LeanSigSignature) -> Result { + let bytes = signature.to_bytes(); + // Ensure we fit into 3112 bytes + if bytes.len() != 3112 { + return Err(anyhow!( + "LeanSigSignature length mismatch: expected 3112, got {}", + bytes.len() + )); + } + let mut inner = [0u8; SIGNATURE_SIZE]; + inner.copy_from_slice(&bytes); + Ok(Self { inner }) + } + + pub fn as_lean_sig(&self) -> anyhow::Result { + println!("Converting Signature to LeanSigSignature..."); + LeanSigSignature::from_bytes(&self.inner) + .map_err(|err| anyhow!("Failed to decode LeanSigSignature from SSZ: {err:?}")) + } + + pub fn verify( + &self, + public_key: &PublicKey, + epoch: u32, + message: &[u8; MESSAGE_LENGTH], + ) -> anyhow::Result { + Ok( + ::verify( + &public_key.as_lean_sig()?, + epoch, + message, + &self.as_lean_sig()?, + ), + ) + } + + /// Debug helper: decode using leansig, then re-encode and ensure bytes match. + pub fn debug_roundtrip(&self) -> anyhow::Result<()> { + let sig = self.as_lean_sig()?; + let re = sig.to_bytes(); + + anyhow::ensure!( + re.as_slice() == self.inner.as_slice(), + "Signature roundtrip mismatch: decoded->encoded bytes differ" + ); + + Ok(()) + } + + /// Debug helper: short stable fingerprint for logs. + pub fn fingerprint_hex(&self) -> String { + use alloy_primitives::hex::ToHexExt; + let bytes = self.inner.as_slice(); + let take = bytes.len().min(12); + format!("0x{}", ToHexExt::encode_hex(&bytes[..take].iter())) + } +} + +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!( + "0x{}", + self.as_lean_sig() + .map_err(serde::ser::Error::custom)? + .to_bytes() + .encode_hex() + )) + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let result: String = Deserialize::deserialize(deserializer)?; + let result = alloy_primitives::hex::decode(&result).map_err(serde::de::Error::custom)?; + + Self::from_lean_sig( + LeanSigSignature::from_bytes(&result) + .map_err(|err| anyhow!("Convert to error, with error trait implemented {err:?}")) + .map_err(serde::de::Error::custom)?, + ) + .map_err(serde::de::Error::custom) + } +} diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index fa13011..5bb8a9e 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -1,19 +1,16 @@ -use crate::attestation::AggregatedAttestations; -use crate::block::BlockSignatures; +use crate::attestation::{AggregatedAttestation, AggregatedAttestations}; use crate::validator::Validator; use crate::{ block::{hash_tree_root, Block, BlockBody, BlockHeader, SignedBlockWithAttestation}, - Attestation, Attestations, Bytes32, Checkpoint, Config, Signature, SignedAttestation, Slot, - Uint64, ValidatorIndex, + Attestation, Bytes32, Checkpoint, Config, Signature, Slot, Uint64, ValidatorIndex, }; use crate::{ HistoricalBlockHashes, JustificationRoots, JustificationsValidators, JustifiedSlots, Validators, }; use serde::{Deserialize, Serialize}; -use ssz::{PersistentList as List, PersistentList}; +use ssz::PersistentList as List; use ssz_derive::Ssz; use std::collections::BTreeMap; -use typenum::U4096; pub const VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 pub const JUSTIFICATION_ROOTS_LIMIT: usize = 1 << 18; // 262144 @@ -111,7 +108,7 @@ impl State { let mut validators = List::default(); for i in 0..num_validators.0 { let validator = Validator { - pubkey: crate::validator::BlsPublicKey::default(), + pubkey: crate::public_key::PublicKey::default(), index: Uint64(i), }; validators.push(validator).expect("Failed to add validator"); @@ -149,9 +146,21 @@ impl State { (self.slot.0 % num_validators) == (index.0 % num_validators) } + /// Get the number of validators (since PersistentList doesn't have len()) + pub fn validator_count(&self) -> usize { + let mut count: u64 = 0; + loop { + match self.validators.get(count) { + Ok(_) => count += 1, + Err(_) => break, + } + } + count as usize + } + pub fn get_justifications(&self) -> BTreeMap> { // Use actual validator count, matching leanSpec - let num_validators = self.validators.len_usize(); + let num_validators = self.validator_count(); (&self.justifications_roots) .into_iter() .enumerate() @@ -174,7 +183,7 @@ impl State { pub fn with_justifications(mut self, map: BTreeMap>) -> Self { // Use actual validator count, matching leanSpec - let num_validators = self.validators.len_usize(); + let num_validators = self.validator_count(); let mut roots: Vec<_> = map.keys().cloned().collect(); roots.sort(); @@ -288,26 +297,12 @@ impl State { pub fn process_block(&self, block: &Block) -> Result { let state = self.process_block_header(block)?; - #[cfg(feature = "devnet1")] - let state_after_ops = state.process_attestations(&block.body.attestations); - #[cfg(feature = "devnet2")] - let state_after_ops = { - let mut unaggregated_attestations = Attestations::default(); - for aggregated_attestation in &block.body.attestations { - let plain_attestations = aggregated_attestation.to_plain(); - // For each attestatio in the vector, push to the list - for attestation in plain_attestations { - unaggregated_attestations - .push(attestation) - .map_err(|e| format!("Failed to push attestation: {:?}", e))?; - } - } - state.process_attestations(&unaggregated_attestations) - }; - // State root validation is handled by state_transition_with_validation when needed + if AggregatedAttestation::has_duplicate_data(&block.body.attestations) { + return Err("Block contains duplicate AttestationData".to_string()); + } - Ok(state_after_ops) + Ok(state.process_attestations(&block.body.attestations)) } pub fn process_block_header(&self, block: &Block) -> Result { @@ -397,143 +392,150 @@ impl State { }) } - pub fn process_attestations(&self, attestations: &Attestations) -> Self { + pub fn process_attestations(&self, attestations: &AggregatedAttestations) -> Self { let mut justifications = self.get_justifications(); let mut latest_justified = self.latest_justified.clone(); let mut latest_finalized = self.latest_finalized.clone(); - // Store initial finalized slot for justifiability checks (per leanSpec) let initial_finalized_slot = self.latest_finalized.slot; let justified_slots = self.justified_slots.clone(); - // PersistentList doesn't expose iter; convert to Vec for simple iteration for now - // Build a temporary Vec by probing sequentially until index error - let mut votes_vec: Vec = Vec::new(); - let mut i: u64 = 0; - loop { - match attestations.get(i) { - Ok(v) => votes_vec.push(v.clone()), - Err(_) => break, - } - i += 1; - } - - // Create mutable working BitList for justified_slots tracking let mut justified_slots_working = Vec::new(); for i in 0..justified_slots.len() { justified_slots_working.push(justified_slots.get(i).map(|b| *b).unwrap_or(false)); } - for attestation in votes_vec.iter() { - let vote = attestation.data.clone(); - let target_slot = vote.target.slot; - let source_slot = vote.source.slot; - let target_root = vote.target.root; - let source_root = vote.source.root; - - let target_slot_int = target_slot.0 as usize; - let source_slot_int = source_slot.0 as usize; - - let source_is_justified = justified_slots_working - .get(source_slot_int) - .copied() - .unwrap_or(false); - let target_already_justified = justified_slots_working - .get(target_slot_int) - .copied() - .unwrap_or(false); - - let source_root_matches_history = self - .historical_block_hashes - .get(source_slot_int as u64) - .map(|root| *root == source_root) - .unwrap_or(false); - - let target_root_matches_history = self - .historical_block_hashes - .get(target_slot_int as u64) - .map(|root| *root == target_root) - .unwrap_or(false); - - let target_is_after_source = target_slot > source_slot; - // Use initial_finalized_slot per leanSpec (not the mutating local copy) - let target_is_justifiable = target_slot.is_justifiable_after(initial_finalized_slot); - - // leanSpec logic: skip if BOTH source and target roots don't match history - // i.e., continue if EITHER matches - let roots_valid = source_root_matches_history || target_root_matches_history; - - let is_valid_vote = source_is_justified - && !target_already_justified - && roots_valid - && target_is_after_source - && target_is_justifiable; - - if !is_valid_vote { - continue; - } + for aggregated_attestation in attestations { + let validator_ids = aggregated_attestation + .aggregation_bits + .to_validator_indices(); + self.process_single_attestation( + &aggregated_attestation.data, + &validator_ids, + &mut justifications, + &mut latest_justified, + &mut latest_finalized, + &mut justified_slots_working, + initial_finalized_slot, + ); + } - if !justifications.contains_key(&target_root) { - // Use actual validator count, not VALIDATOR_REGISTRY_LIMIT - // This matches leanSpec: justifications[target.root] = [Boolean(False)] * self.validators.count - let num_validators = self.validators.len_usize(); - justifications.insert(target_root, vec![false; num_validators]); - } + self.finalize_attestation_processing( + justifications, + latest_justified, + latest_finalized, + justified_slots_working, + ) + } - let validator_id = attestation.validator_id.0 as usize; + /// Process a single attestation's votes. + fn process_single_attestation( + &self, + vote: &crate::attestation::AttestationData, + validator_ids: &[u64], + justifications: &mut BTreeMap>, + latest_justified: &mut Checkpoint, + latest_finalized: &mut Checkpoint, + justified_slots_working: &mut Vec, + initial_finalized_slot: Slot, + ) { + let target_slot = vote.target.slot; + let source_slot = vote.source.slot; + let target_root = vote.target.root; + let source_root = vote.source.root; + + let target_slot_int = target_slot.0 as usize; + let source_slot_int = source_slot.0 as usize; + + let source_is_justified = justified_slots_working + .get(source_slot_int) + .copied() + .unwrap_or(false); + let target_already_justified = justified_slots_working + .get(target_slot_int) + .copied() + .unwrap_or(false); + + let source_root_matches = self + .historical_block_hashes + .get(source_slot_int as u64) + .map(|r| *r == source_root) + .unwrap_or(false); + let target_root_matches = self + .historical_block_hashes + .get(target_slot_int as u64) + .map(|r| *r == target_root) + .unwrap_or(false); + + let is_valid_vote = source_is_justified + && !target_already_justified + && (source_root_matches || target_root_matches) + && target_slot > source_slot + && target_slot.is_justifiable_after(initial_finalized_slot); + + if !is_valid_vote { + return; + } + + if !justifications.contains_key(&target_root) { + justifications.insert(target_root, vec![false; self.validator_count()]); + } + + for &validator_id in validator_ids { + let vid = validator_id as usize; if let Some(votes) = justifications.get_mut(&target_root) { - if validator_id < votes.len() && !votes[validator_id] { - votes[validator_id] = true; - - let num_validators = self.validators.len_u64(); - - let count = votes.iter().filter(|&&v| v).count(); - if 3 * count >= 2 * num_validators as usize { - latest_justified = vote.target; - - // Extend justified_slots_working if needed - while justified_slots_working.len() <= target_slot_int { - justified_slots_working.push(false); - } - justified_slots_working[target_slot_int] = true; - - justifications.remove(&target_root); - - let mut is_finalizable = true; - for s in (source_slot_int + 1)..target_slot_int { - // Use initial_finalized_slot per leanSpec - if Slot(s as u64).is_justifiable_after(initial_finalized_slot) { - is_finalizable = false; - break; - } - } - - if is_finalizable { - latest_finalized = vote.source; - } - } + if vid < votes.len() && !votes[vid] { + votes[vid] = true; } } } - let mut new_state = self.clone().with_justifications(justifications); + if let Some(votes) = justifications.get(&target_root) { + let num_validators = self.validators.len_u64() as usize; + let count = votes.iter().filter(|&&v| v).count(); + if 3 * count >= 2 * num_validators { + *latest_justified = vote.target.clone(); + + justified_slots_working.extend(std::iter::repeat_n( + false, + (target_slot_int + 1).saturating_sub(justified_slots_working.len()), + )); + justified_slots_working[target_slot_int] = true; + + justifications.remove(&target_root); + let is_finalizable = (source_slot_int + 1..target_slot_int) + .all(|s| !Slot(s as u64).is_justifiable_after(initial_finalized_slot)); + + if is_finalizable { + *latest_finalized = vote.source.clone(); + } + } + } + } + + fn finalize_attestation_processing( + &self, + justifications: BTreeMap>, + latest_justified: Checkpoint, + latest_finalized: Checkpoint, + justified_slots_working: Vec, + ) -> Self { + let mut new_state = self.clone().with_justifications(justifications); new_state.latest_justified = latest_justified; new_state.latest_finalized = latest_finalized; - // Convert justified_slots_working Vec back to BitList let mut new_justified_slots = JustifiedSlots::with_length(justified_slots_working.len()); for (i, &val) in justified_slots_working.iter().enumerate() { new_justified_slots.set(i, val); } new_state.justified_slots = new_justified_slots; - new_state } /// Build a valid block on top of this state. /// /// Computes the post-state and creates a block with the correct state root. - /// If `available_signed_attestations` and `known_block_roots` are provided, + /// If `available_attestations` and `known_block_roots` are provided, /// performs fixed-point attestation collection: iteratively adds valid /// attestations until no more can be included. This is necessary because /// processing attestations may update the justified checkpoint, which may @@ -545,48 +547,50 @@ impl State { /// * `proposer_index` - Validator index of the proposer /// * `parent_root` - Root of the parent block (must match state after slot processing) /// * `initial_attestations` - Initial attestations to include - /// * `available_signed_attestations` - Optional pool of attestations to collect from + /// * `available_attestations` - Optional pool of attestations to collect from /// * `known_block_roots` - Optional set of known block roots for attestation validation + /// * `gossip_signatures` - Optional map of individual signatures from gossip + /// * `aggregated_payloads` - Optional map of aggregated signature proofs /// /// # Returns /// - /// Tuple of (Block, post-State, collected attestations, signatures) - #[cfg(feature = "devnet1")] + /// Tuple of (Block, post-State, collected aggregated attestations, aggregated proofs) pub fn build_block( &self, slot: Slot, proposer_index: ValidatorIndex, parent_root: Bytes32, initial_attestations: Option>, - available_signed_attestations: Option<&[SignedBlockWithAttestation]>, + available_attestations: Option>, known_block_roots: Option<&std::collections::HashSet>, + gossip_signatures: Option<&std::collections::HashMap>, + aggregated_payloads: Option< + &std::collections::HashMap>, + >, ) -> Result< ( Block, Self, - Vec, - PersistentList, + Vec, + Vec, ), String, > { - // Initialize empty attestation set for iterative collection + use crate::attestation::{AggregatedAttestation, SignatureKey}; + + // Initialize attestation set let mut attestations = initial_attestations.unwrap_or_default(); - let mut signatures = PersistentList::default(); // Advance state to target slot - // Note: parent_root comes from fork choice and is already validated. - // We cannot validate it against the header hash here because process_slots() - // caches the state root in the header, changing its hash. let pre_state = self.process_slots(slot)?; - // Iteratively collect valid attestations using fixed-point algorithm - // - // Continue until no new attestations can be added to the block. - // This ensures we include the maximal valid attestation set. + // Fixed-point attestation collection loop + // Iteratively add valid attestations until no new ones can be added loop { // Create candidate block with current attestation set - let mut attestations_list = Attestations::default(); - for att in &attestations { + let aggregated = AggregatedAttestation::aggregate_by_data(&attestations); + let mut attestations_list = AggregatedAttestations::default(); + for att in &aggregated { attestations_list .push(att.clone()) .map_err(|e| format!("Failed to push attestation: {:?}", e))?; @@ -605,316 +609,262 @@ impl State { // Apply state transition to get the post-block state let post_state = pre_state.process_block(&candidate_block)?; - // No attestation source provided: done after computing post_state - if available_signed_attestations.is_none() || known_block_roots.is_none() { - // Store the post state root in the block - let final_block = Block { - slot, - proposer_index, - parent_root, - state_root: hash_tree_root(&post_state), - body: candidate_block.body, - }; - return Ok((final_block, post_state, attestations, signatures)); - } + // If no available attestations pool, skip fixed-point iteration + let available = match &available_attestations { + Some(avail) => avail, + None => { + // No fixed-point: compute signatures and return + let (aggregated_attestations, aggregated_proofs) = self + .compute_aggregated_signatures( + &attestations, + gossip_signatures, + aggregated_payloads, + )?; + + let mut final_attestations_list = AggregatedAttestations::default(); + for att in &aggregated_attestations { + final_attestations_list + .push(att.clone()) + .map_err(|e| format!("Failed to push attestation: {:?}", e))?; + } - // Find new valid attestations matching post-state justification - let mut new_attestations = Vec::new(); - let mut new_signatures = Vec::new(); + let final_block = Block { + slot, + proposer_index, + parent_root, + state_root: hash_tree_root(&post_state), + body: BlockBody { + attestations: final_attestations_list, + }, + }; + + return Ok(( + final_block, + post_state, + aggregated_attestations, + aggregated_proofs, + )); + } + }; - let available = available_signed_attestations.unwrap(); - let known_roots = known_block_roots.unwrap(); + // Find new valid attestations from available pool + let mut new_attestations: Vec = Vec::new(); + let current_data_roots: std::collections::HashSet<_> = attestations + .iter() + .map(|a| a.data.data_root_bytes()) + .collect(); - for signed_attestation in available { - let att = &signed_attestation.message.proposer_attestation; - let data = &att.data; + for attestation in available { + // Skip if already included + if current_data_roots.contains(&attestation.data.data_root_bytes()) { + continue; + } - // Skip if target block is unknown - if !known_roots.contains(&data.head.root) { + // Validate attestation against post-state + // Source must match post-state's justified checkpoint + if attestation.data.source != post_state.latest_justified { continue; } - // Skip if attestation source does not match post-state's latest justified - if data.source != post_state.latest_justified { + // Target must be after source + if attestation.data.target.slot <= attestation.data.source.slot { continue; } - // Add attestation if not already included - if !attestations.contains(att) { - new_attestations.push(att.clone()); - // Add corresponding signatures from the signed block - // Note: In the actual implementation, you'd need to properly track - // which signatures correspond to which attestations - let mut idx = 0u64; - loop { - match signed_attestation.signature.get(idx) { - Ok(sig) => { - new_signatures.push(sig.clone()); - idx += 1; - } - Err(_) => break, - } + // Target block must be known (if known_block_roots provided) + if let Some(known_roots) = known_block_roots { + if !known_roots.contains(&attestation.data.target.root) { + continue; } } + + // Check if we have a signature for this attestation + let data_root = attestation.data.data_root_bytes(); + let sig_key = SignatureKey::new(attestation.validator_id.0, data_root); + let has_gossip_sig = + gossip_signatures.map_or(false, |gs| gs.contains_key(&sig_key)); + let has_block_proof = + aggregated_payloads.map_or(false, |ap| ap.contains_key(&sig_key)); + + if has_gossip_sig || has_block_proof { + new_attestations.push(attestation.clone()); + } } // Fixed point reached: no new attestations found if new_attestations.is_empty() { - // Store the post state root in the block + // Compute aggregated signatures + let (aggregated_attestations, aggregated_proofs) = self + .compute_aggregated_signatures( + &attestations, + gossip_signatures, + aggregated_payloads, + )?; + + let mut final_attestations_list = AggregatedAttestations::default(); + for att in &aggregated_attestations { + final_attestations_list + .push(att.clone()) + .map_err(|e| format!("Failed to push attestation: {:?}", e))?; + } + let final_block = Block { slot, proposer_index, parent_root, state_root: hash_tree_root(&post_state), - body: candidate_block.body, + body: BlockBody { + attestations: final_attestations_list, + }, }; - return Ok((final_block, post_state, attestations, signatures)); + + return Ok(( + final_block, + post_state, + aggregated_attestations, + aggregated_proofs, + )); } // Add new attestations and continue iteration attestations.extend(new_attestations); - for sig in new_signatures { - signatures - .push(sig) - .map_err(|e| format!("Failed to push signature: {:?}", e))?; - } } } - #[cfg(feature = "devnet2")] - pub fn build_block( + pub fn compute_aggregated_signatures( &self, - _slot: Slot, - _proposer_index: ValidatorIndex, - _parent_root: Bytes32, - _initial_attestations: Option>, - _available_signed_attestations: Option<&[SignedAttestation]>, - _known_block_roots: Option<&std::collections::HashSet>, - ) -> Result<(Block, Self, Vec, BlockSignatures), String> { - Err("build_block is not implemented for devnet2".to_string()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn proposer_round_robin() { - let st = State::generate_genesis(Uint64(0), Uint64(4)); - assert!(State { - config: st.config.clone(), - ..st.clone() - } - .is_proposer(ValidatorIndex(0))); - } - - #[test] - fn slot_justifiability_rules() { - use crate::slot::Slot; - assert!(Slot(1).is_justifiable_after(Slot(0))); - assert!(Slot(9).is_justifiable_after(Slot(0))); // perfect square - assert!(Slot(6).is_justifiable_after(Slot(0))); // pronic (2*3) - } - - #[test] - fn test_hash_tree_root() { - let body = BlockBody { - attestations: List::default(), - }; - let block = Block { - slot: Slot(1), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32(ssz::H256::zero()), - state_root: Bytes32(ssz::H256::zero()), - body, - }; - - let root = hash_tree_root(&block); - assert_ne!(root, Bytes32(ssz::H256::zero())); - } - - #[test] - fn test_process_slots() { - let genesis_state = State::generate_genesis(Uint64(0), Uint64(10)); - let target_slot = Slot(5); - - let new_state = genesis_state.process_slots(target_slot).unwrap(); - - assert_eq!(new_state.slot, target_slot); - let genesis_state_for_hash = genesis_state.clone(); //this is sooooo bad - assert_eq!( - new_state.latest_block_header.state_root, - hash_tree_root(&genesis_state_for_hash) - ); - } + attestations: &[Attestation], + gossip_signatures: Option<&std::collections::HashMap>, + aggregated_payloads: Option< + &std::collections::HashMap>, + >, + ) -> Result< + ( + Vec, + Vec, + ), + String, + > { + use crate::attestation::{AggregatedAttestation, AggregationBits, SignatureKey}; + use std::collections::HashSet; + + let mut results: Vec<(AggregatedAttestation, crate::AggregatedSignatureProof)> = Vec::new(); + + // Group individual attestations by data + for aggregated in AggregatedAttestation::aggregate_by_data(attestations) { + let data = &aggregated.data; + let data_root = data.data_root_bytes(); + let validator_ids = aggregated.aggregation_bits.to_validator_indices(); + + // Phase 1: Gossip Collection + // Try to collect individual signatures from gossip network + let mut gossip_ids: Vec = Vec::new(); + let mut _gossip_sigs_collected: Vec = Vec::new(); + let mut remaining: HashSet = HashSet::new(); + + if let Some(gossip_sigs) = gossip_signatures { + for vid in &validator_ids { + let key = SignatureKey::new(*vid, data_root); + if let Some(sig) = gossip_sigs.get(&key) { + gossip_ids.push(*vid); + _gossip_sigs_collected.push(sig.clone()); + } else { + remaining.insert(*vid); + } + } + } else { + // No gossip data: all validators need fallback + remaining = validator_ids.iter().copied().collect(); + } - #[test] - #[cfg(feature = "devnet1")] - fn test_build_block() { - // Create genesis state with validators - let genesis_state = State::generate_genesis(Uint64(0), Uint64(4)); - - // Compute expected parent root after slot processing - let pre_state = genesis_state.process_slots(Slot(1)).unwrap(); - let expected_parent_root = hash_tree_root(&pre_state.latest_block_header); - - // Test 1: Build a simple block without attestations - let result = genesis_state.build_block( - Slot(1), - ValidatorIndex(1), - expected_parent_root, - None, - None, - None, - ); + // If we collected any gossip signatures, create an aggregated proof + // NOTE: This matches Python leanSpec behavior (test_mode=True). + // Python also uses test_mode=True with TODO: "Remove test_mode once leanVM + // supports correct signature encoding." + // Once lean-multisig is fully integrated, this will call: + // MultisigAggregatedSignature::aggregate(public_keys, signatures, message, epoch) + if !gossip_ids.is_empty() { + let participants = AggregationBits::from_validator_indices(&gossip_ids); + + // Create proof placeholder (matches Python test_mode behavior) + // TODO: Call actual aggregation when lean-multisig supports proper encoding + let proof_data = crate::MultisigAggregatedSignature::new(Vec::new()) + .expect("Empty proof should always be valid"); + let proof = crate::AggregatedSignatureProof::new(participants.clone(), proof_data); + + results.push(( + AggregatedAttestation { + aggregation_bits: participants, + data: data.clone(), + }, + proof, + )); + } - assert!(result.is_ok(), "Building simple block should succeed"); - let (block, post_state, attestations, signatures) = result.unwrap(); - - // Verify block properties - assert_eq!(block.slot, Slot(1)); - assert_eq!(block.proposer_index, ValidatorIndex(1)); - assert_eq!(block.parent_root, expected_parent_root); - assert_ne!( - block.state_root, - Bytes32(ssz::H256::zero()), - "State root should be computed" - ); + // Phase 2: Fallback to block proofs using greedy set-cover + // Goal: Cover remaining validators with minimum number of proofs + while !remaining.is_empty() { + let payloads = match aggregated_payloads { + Some(p) => p, + None => break, + }; - // Verify attestations and signatures are empty - assert_eq!(attestations.len(), 0); - // Check signatures by trying to get first element - assert!(signatures.get(0).is_err(), "Signatures should be empty"); - - // Verify post-state has advanced - assert_eq!(post_state.slot, Slot(1)); - // Note: The post-state's latest_block_header.state_root is zero because it will be - // filled in during the next slot processing - assert_eq!( - block.parent_root, expected_parent_root, - "Parent root should match" - ); + // Pick any remaining validator to find candidate proofs + let target_id = *remaining.iter().next().unwrap(); + let key = SignatureKey::new(target_id, data_root); - // Test 2: Build block with initial attestations - let attestation = Attestation { - validator_id: Uint64(0), - data: crate::AttestationData { - slot: Slot(1), - head: Checkpoint { - root: expected_parent_root, - slot: Slot(0), - }, - target: Checkpoint { - root: expected_parent_root, - slot: Slot(1), - }, - source: Checkpoint { - root: expected_parent_root, - slot: Slot(0), - }, - }, - }; + let candidates = match payloads.get(&key) { + Some(proofs) if !proofs.is_empty() => proofs, + _ => break, // No proofs found for this validator + }; - let result = genesis_state.build_block( - Slot(1), - ValidatorIndex(1), - expected_parent_root, - Some(vec![attestation.clone()]), - None, - None, - ); + // Greedy selection: find proof covering most remaining validators + // For each candidate proof, compute intersection with remaining validators + let (best_proof, covered_set) = candidates + .iter() + .map(|proof| { + let proof_validators: HashSet = + proof.get_participant_indices().into_iter().collect(); + let intersection: HashSet = + remaining.intersection(&proof_validators).copied().collect(); + (proof, intersection) + }) + .max_by_key(|(_, intersection)| intersection.len()) + .expect("candidates is non-empty"); - assert!( - result.is_ok(), - "Building block with attestations should succeed" - ); - let (block, _post_state, attestations, _signatures) = result.unwrap(); - - // Verify attestation was included - assert_eq!(attestations.len(), 1); - assert_eq!(attestations[0].validator_id, Uint64(0)); - // Check that attestation list has one element - assert!( - block.body.attestations.get(0).is_ok(), - "Block should contain attestation" - ); - assert!( - block.body.attestations.get(1).is_err(), - "Block should have only one attestation" - ); - } + // Guard: If best proof has zero overlap, stop + if covered_set.is_empty() { + break; + } - #[test] - #[cfg(feature = "devnet1")] - fn test_build_block_advances_state() { - // Create genesis state - let genesis_state = State::generate_genesis(Uint64(0), Uint64(10)); - - // Compute parent root after advancing to target slot - let pre_state = genesis_state.process_slots(Slot(5)).unwrap(); - let parent_root = hash_tree_root(&pre_state.latest_block_header); - - // Build block at slot 5 - // Proposer for slot 5 with 10 validators is (5 % 10) = 5 - let result = - genesis_state.build_block(Slot(5), ValidatorIndex(5), parent_root, None, None, None); - - assert!(result.is_ok()); - let (block, post_state, _, _) = result.unwrap(); - - // Verify state advanced through slots - assert_eq!(post_state.slot, Slot(5)); - assert_eq!(block.slot, Slot(5)); - - // Verify block can be applied to genesis state - let transition_result = genesis_state.state_transition_with_validation( - SignedBlockWithAttestation { - message: crate::BlockWithAttestation { - block: block.clone(), - proposer_attestation: Attestation::default(), - }, - signature: PersistentList::default(), - }, - true, // signatures are considered valid (not validating, just marking as valid) - true, - ); + // Record proof with its actual participants (from the proof itself) + let covered_validators: Vec = best_proof.get_participant_indices(); + let participants = AggregationBits::from_validator_indices(&covered_validators); + + results.push(( + AggregatedAttestation { + aggregation_bits: participants, + data: data.clone(), + }, + best_proof.clone(), + )); + + // Remove covered validators from remaining + for vid in &covered_set { + remaining.remove(vid); + } + } + } - assert!( - transition_result.is_ok(), - "Built block should be valid for state transition" - ); - } + // Handle empty case + if results.is_empty() { + return Ok((Vec::new(), Vec::new())); + } - #[test] - #[cfg(feature = "devnet1")] - fn test_build_block_state_root_matches() { - // Create genesis state - let genesis_state = State::generate_genesis(Uint64(0), Uint64(3)); - - // Compute parent root after advancing to target slot - let pre_state = genesis_state.process_slots(Slot(1)).unwrap(); - let parent_root = hash_tree_root(&pre_state.latest_block_header); - - // Build a block - // Proposer for slot 1 with 3 validators is (1 % 3) = 1 - let result = - genesis_state.build_block(Slot(1), ValidatorIndex(1), parent_root, None, None, None); - - assert!(result.is_ok()); - let (block, post_state, _, _) = result.unwrap(); - - // Verify the state root in block matches the computed post-state - let computed_state_root = hash_tree_root(&post_state); - assert_eq!( - block.state_root, computed_state_root, - "Block state root should match computed post-state root" - ); + // Unzip results into parallel lists + let (aggregated_attestations, aggregated_proofs): (Vec<_>, Vec<_>) = + results.into_iter().unzip(); - // Verify it's not zero - assert_ne!( - block.state_root, - Bytes32(ssz::H256::zero()), - "State root should not be zero" - ); + Ok((aggregated_attestations, aggregated_proofs)) } } diff --git a/lean_client/containers/src/validator.rs b/lean_client/containers/src/validator.rs index 2649f55..a6580da 100644 --- a/lean_client/containers/src/validator.rs +++ b/lean_client/containers/src/validator.rs @@ -1,81 +1,10 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use ssz::ByteVector; +use serde::{Deserialize, Serialize}; use ssz_derive::Ssz; -use typenum::U52; - -/// BLS public key - 52 bytes (as defined in lean spec) -#[derive(Clone, Debug, PartialEq, Eq, Ssz)] -#[ssz(transparent)] -pub struct BlsPublicKey(pub ByteVector); - -impl Default for BlsPublicKey { - fn default() -> Self { - BlsPublicKey(ByteVector::default()) - } -} - -// Custom serde implementation -impl Serialize for BlsPublicKey { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // ByteVector might have to_vec() or similar - // For now, use unsafe to access the underlying bytes - let bytes = unsafe { - std::slice::from_raw_parts(&self.0 as *const ByteVector as *const u8, 52) - }; - let hex_string = format!("0x{}", hex::encode(bytes)); - serializer.serialize_str(&hex_string) - } -} - -impl<'de> Deserialize<'de> for BlsPublicKey { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - let s = s.strip_prefix("0x").unwrap_or(&s); - - let decoded = hex::decode(s).map_err(serde::de::Error::custom)?; - if decoded.len() != 52 { - return Err(serde::de::Error::custom(format!( - "Expected 52 bytes, got {}", - decoded.len() - ))); - } - - // Create ByteVector from decoded bytes using unsafe - let mut byte_vec = ByteVector::default(); - unsafe { - let dest = &mut byte_vec as *mut ByteVector as *mut u8; - std::ptr::copy_nonoverlapping(decoded.as_ptr(), dest, 52); - } - - Ok(BlsPublicKey(byte_vec)) - } -} - -impl BlsPublicKey { - pub fn from_hex(s: &str) -> Result { - let s = s.strip_prefix("0x").unwrap_or(s); - let decoded = hex::decode(s).map_err(|e| e.to_string())?; - if decoded.len() != 52 { - return Err(format!("Expected 52 bytes, got {}", decoded.len())); - } - let mut byte_vec = ByteVector::default(); - unsafe { - let dest = &mut byte_vec as *mut ByteVector as *mut u8; - std::ptr::copy_nonoverlapping(decoded.as_ptr(), dest, 52); - } - Ok(BlsPublicKey(byte_vec)) - } -} #[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] pub struct Validator { - pub pubkey: BlsPublicKey, + // This now uses new XMSS PublicKey struct + pub pubkey: crate::public_key::PublicKey, #[serde(default)] pub index: crate::Uint64, } diff --git a/lean_client/containers/tests/test_vectors/block_processing.rs b/lean_client/containers/tests/test_vectors/block_processing.rs index 5bbc997..e3325cb 100644 --- a/lean_client/containers/tests/test_vectors/block_processing.rs +++ b/lean_client/containers/tests/test_vectors/block_processing.rs @@ -1,6 +1,14 @@ -// Integration test: All block processing test vectors +// Integration test: All block processing test vectors for devnet2 format +// +// NOTE: These tests are currently disabled because the JSON test vector files +// use a wrapper format (e.g., "validators": {"data": [...]}) that doesn't match +// the Rust deserializer expectations (which expects a direct list). +// The test vectors need to be regenerated by leanSpec to match the expected format, +// or the deserialization logic needs to be updated. + use super::runner::TestRunner; +/* #[test] #[cfg(feature = "devnet1")] fn test_process_first_block_after_genesis() { @@ -79,3 +87,4 @@ fn test_block_with_invalid_state_root() { TestRunner::run_block_processing_test(test_path) .expect("test_block_with_invalid_state_root failed"); } +*/ diff --git a/lean_client/containers/tests/test_vectors/mod.rs b/lean_client/containers/tests/test_vectors/mod.rs index acdc055..5d26d0c 100644 --- a/lean_client/containers/tests/test_vectors/mod.rs +++ b/lean_client/containers/tests/test_vectors/mod.rs @@ -1,3 +1,7 @@ +//! Test vector modules for devnet2 format +//! +//! Contains test runners and test cases for block processing, genesis, and signature verification. + // Test vector modules pub mod block_processing; pub mod genesis; diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index f6942fe..7459538 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -244,7 +244,17 @@ impl TestRunner { println!("\nProcessing single empty block at slot {:?}", block.slot); // Verify it's an empty block (no attestations) - let attestation_count = block.body.attestations.len_u64(); + let attestation_count = { + let mut count = 0u64; + loop { + match block.body.attestations.get(count) { + Ok(_) => count += 1, + Err(_) => break, + } + } + count + }; + if attestation_count > 0 { return Err(format!( "Expected empty block, but found {} attestations", @@ -388,7 +398,17 @@ impl TestRunner { return Err(format!("Block {} parent_root mismatch", idx + 1).into()); } - let attestation_count = block.body.attestations.len_u64(); + // Check if block is empty (no attestations) + let attestation_count = { + let mut count = 0u64; + loop { + match block.body.attestations.get(count) { + Ok(_) => count += 1, + Err(_) => break, + } + } + count + }; // Process the full block (header + operations) let result = state_after_slots.process_block(block); @@ -596,9 +616,11 @@ impl TestRunner { Ok(()) } - /// Test runner for verify_signatures test vectors - /// Tests XMSS signature verification on SignedBlockWithAttestation - #[cfg(feature = "devnet1")] + // Test runner for verify_signatures test vectors + // Tests XMSS signature verification on SignedBlockWithAttestation + // + // NOTE: Disabled until test vector files are regenerated for devnet2 BlockSignatures format. + // The current JSON test vectors use signature.data array instead of attestation_signatures + proposer_signature. pub fn run_verify_signatures_test>( path: P, ) -> Result<(), Box> { @@ -633,9 +655,6 @@ impl TestRunner { signed_block.message.proposer_attestation.validator_id.0 ); - let signature_count = signed_block.signature.len_u64(); - println!(" Signatures: {}", signature_count); - // Check if we expect this test to fail if let Some(ref exception) = test_case.expect_exception { println!(" Expecting exception: {}", exception); diff --git a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs index 72d48b4..5d1e4dc 100644 --- a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs +++ b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "devnet2")] #[cfg(test)] mod tests { use containers::attestation::{ diff --git a/lean_client/containers/tests/unit_tests/common.rs b/lean_client/containers/tests/unit_tests/common.rs index 1a648b8..2981a2c 100644 --- a/lean_client/containers/tests/unit_tests/common.rs +++ b/lean_client/containers/tests/unit_tests/common.rs @@ -1,3 +1,8 @@ +//! Common test utilities for devnet2 format +//! +//! Helper functions for creating test blocks, states, and attestations +//! using the devnet2 data structures. + use containers::block::BlockSignatures; use containers::{ block::{hash_tree_root, Block, BlockBody, BlockHeader}, @@ -23,11 +28,6 @@ pub fn create_block( parent_header: &mut BlockHeader, attestations: Option, ) -> SignedBlockWithAttestation { - #[cfg(feature = "devnet1")] - let body = BlockBody { - attestations: attestations.unwrap_or_else(PersistentList::default), - }; - #[cfg(feature = "devnet2")] let body = BlockBody { attestations: { let attestations_vec = attestations.unwrap_or_default(); @@ -39,9 +39,6 @@ pub fn create_block( let aggregated: Vec = AggregatedAttestation::aggregate_by_data(&attestations_vec); - let aggregated: Vec = - AggregatedAttestation::aggregate_by_data(&attestations_vec); - // Create a new empty PersistentList let mut persistent_list: PersistentList = PersistentList::default(); @@ -55,7 +52,6 @@ pub fn create_block( persistent_list }, - // other BlockBody fields... }; let block_message = Block { @@ -66,16 +62,6 @@ pub fn create_block( body: body, }; - #[cfg(feature = "devnet1")] - let return_value = SignedBlockWithAttestation { - message: BlockWithAttestation { - block: block_message, - proposer_attestation: Attestation::default(), - }, - signature: PersistentList::default(), - }; - - #[cfg(feature = "devnet2")] let return_value = SignedBlockWithAttestation { message: BlockWithAttestation { block: block_message, diff --git a/lean_client/containers/tests/unit_tests/mod.rs b/lean_client/containers/tests/unit_tests/mod.rs index 1bef390..315792d 100644 --- a/lean_client/containers/tests/unit_tests/mod.rs +++ b/lean_client/containers/tests/unit_tests/mod.rs @@ -1,7 +1,12 @@ // tests/unit_tests/mod.rs + +// Modules that work with both devnet1 and devnet2 mod attestation_aggregation; -mod common; mod state_basic; mod state_justifications; + +// TODO: Update these modules for devnet2 data structures +// (SignedAttestation now uses AttestationData directly, BlockSignatures changed, etc.) +mod common; mod state_process; mod state_transition; diff --git a/lean_client/containers/tests/unit_tests/state_basic.rs b/lean_client/containers/tests/unit_tests/state_basic.rs index 085384a..5fa59f3 100644 --- a/lean_client/containers/tests/unit_tests/state_basic.rs +++ b/lean_client/containers/tests/unit_tests/state_basic.rs @@ -1,3 +1,7 @@ +//! State basic tests for devnet2 format +//! +//! Tests for genesis generation, proposer selection, slot rules, and hash tree root. + // tests/state_basic.rs use containers::{ block::{hash_tree_root, BlockBody}, diff --git a/lean_client/containers/tests/unit_tests/state_justifications.rs b/lean_client/containers/tests/unit_tests/state_justifications.rs index afdd220..61bf09c 100644 --- a/lean_client/containers/tests/unit_tests/state_justifications.rs +++ b/lean_client/containers/tests/unit_tests/state_justifications.rs @@ -1,3 +1,7 @@ +//! State justifications tests for devnet2 format +//! +//! Tests for justification get/set operations and roundtrip verification. + // tests/state_justifications.rs use containers::{state::State, types::Bytes32, Config}; use pretty_assertions::assert_eq; diff --git a/lean_client/containers/tests/unit_tests/state_process.rs b/lean_client/containers/tests/unit_tests/state_process.rs index 5df98cf..632b0b5 100644 --- a/lean_client/containers/tests/unit_tests/state_process.rs +++ b/lean_client/containers/tests/unit_tests/state_process.rs @@ -128,72 +128,3 @@ fn test_process_block_header_invalid( let err_msg = result.unwrap_err(); assert!(err_msg.contains(expected_error)); } - -// This test verifies that attestations correctly justify and finalize slots -#[cfg(feature = "devnet1")] -#[test] -fn test_process_attestations_justification_and_finalization() { - let mut state = genesis_state(); - - // Process slot 1 and block - let mut state_at_slot_1 = state.process_slots(Slot(1)).unwrap(); - let block1 = create_block(1, &mut state_at_slot_1.latest_block_header, None); - // Use process_block_header and process_operations separately to avoid state root validation - let state_after_header1 = state_at_slot_1 - .process_block_header(&block1.message.block) - .unwrap(); - state = state_after_header1.process_attestations(&block1.message.block.body.attestations); - - // Process slot 4 and block - let mut state_at_slot_4 = state.process_slots(Slot(4)).unwrap(); - let block4 = create_block(4, &mut state_at_slot_4.latest_block_header, None); - let state_after_header4 = state_at_slot_4 - .process_block_header(&block4.message.block) - .unwrap(); - state = state_after_header4.process_attestations(&block4.message.block.body.attestations); - - // Advance to slot 5 - state = state.process_slots(Slot(5)).unwrap(); - - let genesis_checkpoint = Checkpoint { - root: *state.historical_block_hashes.get(0).unwrap(), - slot: Slot(0), - }; - - let checkpoint4 = Checkpoint { - root: hash_tree_root(&state.latest_block_header), - slot: Slot(4), - }; - - let attestations_for_4: Vec = (0..7) - .map(|i| Attestation { - validator_id: Uint64(i), - data: AttestationData { - slot: Slot(4), - head: checkpoint4.clone(), - target: checkpoint4.clone(), - source: genesis_checkpoint.clone(), - }, - }) - .collect(); - - // Convert Vec to PersistentList - let mut attestations_list: List<_, U4096> = List::default(); - for a in attestations_for_4 { - attestations_list.push(a).unwrap(); - } - - let new_state = state.process_attestations(&attestations_list); - - assert_eq!(new_state.latest_justified, checkpoint4); - let justified_slot_4 = new_state - .justified_slots - .get(4) - .map(|b| *b) - .unwrap_or(false); - assert_eq!(justified_slot_4, true); - assert_eq!(new_state.latest_finalized, genesis_checkpoint); - assert!(!new_state - .get_justifications() - .contains_key(&checkpoint4.root)); -} diff --git a/lean_client/containers/tests/unit_tests/state_transition.rs b/lean_client/containers/tests/unit_tests/state_transition.rs index 7725210..4b763c6 100644 --- a/lean_client/containers/tests/unit_tests/state_transition.rs +++ b/lean_client/containers/tests/unit_tests/state_transition.rs @@ -1,9 +1,16 @@ +//! State transition tests +//! +//! Tests for full state transitions including signature validation +//! and state root verification. + // tests/state_transition.rs use containers::{ - block::{hash_tree_root, Block, BlockWithAttestation, SignedBlockWithAttestation}, + block::{ + hash_tree_root, Block, BlockSignatures, BlockWithAttestation, SignedBlockWithAttestation, + }, state::State, types::{Bytes32, Uint64}, - Attestation, Attestations, Slot, + Attestation, Signature, Slot, }; use pretty_assertions::assert_eq; use rstest::fixture; @@ -31,7 +38,6 @@ fn test_state_transition_full() { // Use process_block_header + process_operations to avoid state root validation during setup let state_after_header = state_at_slot_1.process_block_header(&block).unwrap(); - #[cfg(feature = "devnet1")] let expected_state = state_after_header.process_attestations(&block.body.attestations); #[cfg(feature = "devnet2")] @@ -79,7 +85,6 @@ fn test_state_transition_invalid_signatures() { // Use process_block_header + process_operations to avoid state root validation during setup let state_after_header = state_at_slot_1.process_block_header(&block).unwrap(); - #[cfg(feature = "devnet1")] let expected_state = state_after_header.process_attestations(&block.body.attestations); #[cfg(feature = "devnet2")] @@ -113,7 +118,7 @@ fn test_state_transition_invalid_signatures() { assert_eq!(result.unwrap_err(), "Block signatures must be valid"); } -#[cfg(feature = "devnet1")] +// Test with bad state root using devnet2 BlockSignatures structure #[test] fn test_state_transition_bad_state_root() { let state = genesis_state(); @@ -130,7 +135,10 @@ fn test_state_transition_bad_state_root() { block, proposer_attestation: Attestation::default(), }, - signature: PersistentList::default(), + signature: BlockSignatures { + attestation_signatures: PersistentList::default(), + proposer_signature: Signature::default(), + }, }; let result = state.state_transition(final_signed_block_with_attestation, true); @@ -138,7 +146,6 @@ fn test_state_transition_bad_state_root() { assert_eq!(result.unwrap_err(), "Invalid block state root"); } -#[cfg(feature = "devnet2")] #[test] fn test_state_transition_devnet2() { let state = genesis_state(); @@ -152,22 +159,8 @@ fn test_state_transition_devnet2() { // Process the block header and attestations let state_after_header = state_at_slot_1.process_block_header(&block).unwrap(); - #[cfg(feature = "devnet1")] let expected_state = state_after_header.process_attestations(&block.body.attestations); - #[cfg(feature = "devnet2")] - let expected_state = { - let mut unaggregated_attestations = Attestations::default(); - for aggregated_attestation in &block.body.attestations { - let plain_attestations = aggregated_attestation.to_plain(); - // For each attestatio in the vector, push to the list - for attestation in plain_attestations { - unaggregated_attestations.push(attestation); - } - } - state_after_header.process_attestations(&unaggregated_attestations) - }; - // Ensure the state root matches the expected state let block_with_correct_root = Block { state_root: hash_tree_root(&expected_state), diff --git a/lean_client/env-config/Cargo.toml b/lean_client/env-config/Cargo.toml index 4b761e5..bd3e737 100644 --- a/lean_client/env-config/Cargo.toml +++ b/lean_client/env-config/Cargo.toml @@ -5,8 +5,4 @@ edition.workspace = true authors.workspace = true license.workspace = true -[features] -devnet1 = [] -devnet2 = [] - [dependencies] diff --git a/lean_client/fork_choice/Cargo.toml b/lean_client/fork_choice/Cargo.toml index b16f561..f707648 100644 --- a/lean_client/fork_choice/Cargo.toml +++ b/lean_client/fork_choice/Cargo.toml @@ -5,17 +5,8 @@ edition = "2021" [features] default = [] -devnet1 = ["containers/devnet1", "env-config/devnet1"] -devnet2 = ["containers/devnet2", "env-config/devnet1"] [dependencies] env-config = { path = "../env-config", default-features = false } containers = { path = "../containers", default-features = false } -ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop"} -ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop" } -typenum = "1.17.0" -serde = { version = "1.0", features = ["derive"] } - -[dev-dependencies] -ssz_rs = "0.9" -serde_json = "1.0" +ssz = { workspace = true } diff --git a/lean_client/fork_choice/src/handlers.rs b/lean_client/fork_choice/src/handlers.rs index 9f3837d..3054052 100644 --- a/lean_client/fork_choice/src/handlers.rs +++ b/lean_client/fork_choice/src/handlers.rs @@ -1,4 +1,5 @@ use crate::store::*; +use containers::SignatureKey; use containers::{ attestation::SignedAttestation, block::SignedBlockWithAttestation, Bytes32, ValidatorIndex, }; @@ -25,22 +26,9 @@ pub fn on_attestation( signed_attestation: SignedAttestation, is_from_block: bool, ) -> Result<(), String> { - #[cfg(feature = "devnet1")] - let validator_id = ValidatorIndex(signed_attestation.message.validator_id.0); - #[cfg(feature = "devnet1")] - let attestation_slot = signed_attestation.message.data.slot; - #[cfg(feature = "devnet1")] - let source_slot = signed_attestation.message.data.source.slot; - #[cfg(feature = "devnet1")] - let target_slot = signed_attestation.message.data.target.slot; - - #[cfg(feature = "devnet2")] let validator_id = ValidatorIndex(signed_attestation.validator_id); - #[cfg(feature = "devnet2")] let attestation_slot = signed_attestation.message.slot; - #[cfg(feature = "devnet2")] let source_slot = signed_attestation.message.source.slot; - #[cfg(feature = "devnet2")] let target_slot = signed_attestation.message.target.slot; // Validate attestation is not from future @@ -62,20 +50,6 @@ pub fn on_attestation( if is_from_block { // On-chain attestation processing - immediately becomes "known" - #[cfg(feature = "devnet1")] - if store - .latest_known_attestations - .get(&validator_id) - .map_or(true, |existing| { - existing.message.data.slot < attestation_slot - }) - { - store - .latest_known_attestations - .insert(validator_id, signed_attestation.clone()); - } - - #[cfg(feature = "devnet2")] if store .latest_known_attestations .get(&validator_id) @@ -88,31 +62,20 @@ pub fn on_attestation( // Remove from new attestations if superseded if let Some(existing_new) = store.latest_new_attestations.get(&validator_id) { - #[cfg(feature = "devnet1")] - if existing_new.message.data.slot <= attestation_slot { - store.latest_new_attestations.remove(&validator_id); - } - #[cfg(feature = "devnet2")] if existing_new.message.slot <= attestation_slot { store.latest_new_attestations.remove(&validator_id); } } } else { // Network gossip attestation processing - goes to "new" stage - #[cfg(feature = "devnet1")] - if store - .latest_new_attestations - .get(&validator_id) - .map_or(true, |existing| { - existing.message.data.slot < attestation_slot - }) - { - store - .latest_new_attestations - .insert(validator_id, signed_attestation); - } + // Store signature for later aggregation during block building + let data_root = signed_attestation.message.data_root_bytes(); + let sig_key = SignatureKey::new(signed_attestation.validator_id, data_root); + store + .gossip_signatures + .insert(sig_key, signed_attestation.signature.clone()); - #[cfg(feature = "devnet2")] + // Track attestation for fork choice if store .latest_new_attestations .get(&validator_id) @@ -186,97 +149,74 @@ fn process_block_internal( } // Process block body attestations as on-chain (is_from_block=true) - let attestations = &signed_block.message.block.body.attestations; let signatures = &signed_block.signature; - #[cfg(feature = "devnet1")] - { - for i in 0.. { - match (attestations.get(i), signatures.get(i)) { - (Ok(attestation), Ok(signature)) => { - let signed_attestation = SignedAttestation { - message: attestation.clone(), - signature: signature.clone(), - }; - on_attestation(store, signed_attestation, true)?; + let aggregated_attestations = &signed_block.message.block.body.attestations; + let proposer_attestation = &signed_block.message.proposer_attestation; + + // Store aggregated proofs for future block building + // Each attestation_signature proof is indexed by (validator_id, data_root) for each participating validator + for (att_idx, aggregated_attestation) in aggregated_attestations.into_iter().enumerate() { + let data_root = aggregated_attestation.data.data_root_bytes(); + + // Get the corresponding proof from attestation_signatures + if let Ok(proof_data) = signatures.attestation_signatures.get(att_idx as u64) { + // Store proof for each validator in the aggregation + for (bit_idx, bit) in aggregated_attestation.aggregation_bits.0.iter().enumerate() { + if *bit { + let validator_id = bit_idx as u64; + let sig_key = SignatureKey::new(validator_id, data_root); + store + .aggregated_payloads + .entry(sig_key) + .or_insert_with(Vec::new) + .push(proof_data.clone()); } - _ => break, } } - - // Update head BEFORE processing proposer attestation - update_head(store); - - // Process proposer attestation as gossip (is_from_block=false) - // This ensures it goes to "new" attestations and doesn't immediately affect fork choice - let num_body_attestations = attestations.len_u64(); - - // Get proposer signature or use default if not present (for tests) - use containers::attestation::Signature; - let proposer_signature = signatures - .get(num_body_attestations) - .map(|sig| sig.clone()) - .unwrap_or_else(|_| Signature::default()); - - let proposer_signed_attestation = SignedAttestation { - message: signed_block.message.proposer_attestation.clone(), - signature: proposer_signature, - }; - - // Process proposer attestation as if received via gossip (is_from_block=false) - // This ensures it goes to "new" attestations and doesn't immediately affect fork choice - on_attestation(store, proposer_signed_attestation, false)?; - - Ok(()) } - #[cfg(feature = "devnet2")] - { - let aggregated_attestations = &signed_block.message.block.body.attestations; - let attestation_signatures = &signed_block.signature.attestation_signatures; - let proposer_attestation = &signed_block.message.proposer_attestation; - - for (aggregated_attestation, aggregated_signature) in aggregated_attestations - .into_iter() - .zip(attestation_signatures) - { - let validator_ids: Vec = aggregated_attestation - .aggregation_bits - .0 - .iter() - .enumerate() - .filter(|(_, bit)| **bit) - .map(|(index, _)| index as u64) - .collect(); - - for (validator_id, signature) in validator_ids.into_iter().zip(aggregated_signature) { - on_attestation( - store, - SignedAttestation { - validator_id, - message: aggregated_attestation.data.clone(), - signature: *signature, - }, - true, - )?; - } + // Process each aggregated attestation's validators for fork choice + // Note: Signature verification is done in verify_signatures() before on_block() + for aggregated_attestation in aggregated_attestations.into_iter() { + let validator_ids: Vec = aggregated_attestation + .aggregation_bits + .0 + .iter() + .enumerate() + .filter(|(_, bit)| **bit) + .map(|(index, _)| index as u64) + .collect(); + + // Each validator in the aggregation votes for this attestation data + for validator_id in validator_ids { + on_attestation( + store, + SignedAttestation { + validator_id, + message: aggregated_attestation.data.clone(), + // Use a default signature since verification already happened + signature: containers::Signature::default(), + }, + true, + )?; } + } - // Update head BEFORE processing proposer attestation - update_head(store); + // Update head BEFORE processing proposer attestation + update_head(store); - let proposer_signed_attestation = SignedAttestation { - validator_id: proposer_attestation.validator_id.0, - message: proposer_attestation.data.clone(), - signature: signed_block.signature.proposer_signature, - }; + let proposer_signed_attestation = SignedAttestation { + validator_id: proposer_attestation.validator_id.0, + message: proposer_attestation.data.clone(), + signature: signed_block.signature.proposer_signature, + }; - // Process proposer attestation as if received via gossip (is_from_block=false) - // This ensures it goes to "new" attestations and doesn't immediately affect fork choice - on_attestation(store, proposer_signed_attestation, false)?; + // Process proposer attestation as if received via gossip (is_from_block=false) + // This ensures it goes to "new" attestations and doesn't immediately affect fork choice + on_attestation(store, proposer_signed_attestation, false)?; - Ok(()) - } + Ok(()) } fn process_pending_blocks(store: &mut Store, mut roots: Vec) { diff --git a/lean_client/fork_choice/src/store.rs b/lean_client/fork_choice/src/store.rs index 51b26b6..818b57d 100644 --- a/lean_client/fork_choice/src/store.rs +++ b/lean_client/fork_choice/src/store.rs @@ -2,6 +2,7 @@ use containers::{ attestation::SignedAttestation, block::SignedBlockWithAttestation, checkpoint::Checkpoint, config::Config, state::State, Bytes32, Root, Slot, ValidatorIndex, }; +use containers::{AggregatedSignatureProof, Signature, SignatureKey}; use ssz::SszHash; use std::collections::HashMap; pub type Interval = u64; @@ -22,6 +23,10 @@ pub struct Store { pub latest_known_attestations: HashMap, pub latest_new_attestations: HashMap, pub blocks_queue: HashMap>, + + pub gossip_signatures: HashMap, + + pub aggregated_payloads: HashMap>, } pub fn get_forkchoice_store( @@ -62,6 +67,8 @@ pub fn get_forkchoice_store( latest_known_attestations: HashMap::new(), latest_new_attestations: HashMap::new(), blocks_queue: HashMap::new(), + gossip_signatures: HashMap::new(), + aggregated_payloads: HashMap::new(), } } @@ -84,9 +91,6 @@ pub fn get_fork_choice_head( // stage 1: accumulate weights by walking up from each attestation's head for attestation in latest_attestations.values() { - #[cfg(feature = "devnet1")] - let mut curr = attestation.message.data.head.root; - #[cfg(feature = "devnet2")] let mut curr = attestation.message.head.root; if let Some(block) = store.blocks.get(&curr) { @@ -250,3 +254,89 @@ pub fn get_proposal_head(store: &mut Store, slot: Slot) -> Root { accept_new_attestations(store); store.head } + +/// Produce a block and aggregated signature proofs for the target slot. +/// +/// The proposer returns the block and `MultisigAggregatedSignature` proofs aligned +/// with `block.body.attestations` so it can craft `SignedBlockWithAttestation`. +/// +/// # Algorithm Overview +/// 1. **Get Proposal Head**: Retrieve current chain head as parent +/// 2. **Collect Attestations**: Convert known attestations to plain attestations +/// 3. **Build Block**: Use State.build_block with signature caches +/// 4. **Store Block**: Insert block and post-state into Store +/// +/// # Arguments +/// * `store` - Mutable reference to the fork choice store +/// * `slot` - Target slot number for block production +/// * `validator_index` - Index of validator authorized to propose this block +/// +/// # Returns +/// Tuple of (block root, finalized Block, attestation signature proofs) +pub fn produce_block_with_signatures( + store: &mut Store, + slot: Slot, + validator_index: ValidatorIndex, +) -> Result< + ( + Root, + containers::block::Block, + Vec, + ), + String, +> { + use containers::Attestation; + + // Get parent block head + let head_root = get_proposal_head(store, slot); + let head_state = store + .states + .get(&head_root) + .ok_or_else(|| "Head state not found".to_string())? + .clone(); + + // Validate proposer authorization for this slot + let num_validators = head_state.validators.len_u64(); + let expected_proposer = slot.0 % num_validators; + if validator_index.0 != expected_proposer { + return Err(format!( + "Validator {} is not the proposer for slot {} (expected {})", + validator_index.0, slot.0, expected_proposer + )); + } + + // Convert AttestationData to Attestation objects for build_block + let available_attestations: Vec = store + .latest_known_attestations + .iter() + .map(|(validator_idx, signed_att)| Attestation { + validator_id: containers::Uint64(validator_idx.0), + data: signed_att.message.clone(), + }) + .collect(); + + // Get known block roots for attestation validation + let known_block_roots: std::collections::HashSet = + store.blocks.keys().copied().collect(); + + // Build block with fixed-point attestation collection and signature aggregation + let (final_block, final_post_state, _aggregated_attestations, signatures) = head_state + .build_block( + slot, + validator_index, + head_root, + None, // initial_attestations - start with empty, let fixed-point collect + Some(available_attestations), + Some(&known_block_roots), + Some(&store.gossip_signatures), + Some(&store.aggregated_payloads), + )?; + + // Compute block root + let block_root = Bytes32(final_block.hash_tree_root()); + + // Store block and state + store.states.insert(block_root, final_post_state); + + Ok((block_root, final_block, signatures)) +} diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index 85683af..6ac6438 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -1,870 +1,392 @@ -use fork_choice::{ - handlers::{on_attestation, on_block, on_tick}, - store::{get_forkchoice_store, Store}, -}; +//! Fork choice test vectors for devnet2 +//! +//! Integration tests for fork choice rule implementation +//! using devnet2 data structures. use containers::{ - attestation::{Attestation, AttestationData, Signature, SignedAttestation}, - block::{ - hash_tree_root, Block, BlockBody, BlockHeader, BlockWithAttestation, - SignedBlockWithAttestation, - }, + attestation::{AttestationData, SignedAttestation}, + block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, checkpoint::Checkpoint, config::Config, state::State, + validator::Validator, Bytes32, Slot, Uint64, ValidatorIndex, }; - -use serde::Deserialize; -use ssz::{PersistentList, SszHash}; +use fork_choice::store::{get_fork_choice_head, get_forkchoice_store, Store}; +use ssz::SszHash; use std::collections::HashMap; -use std::panic::AssertUnwindSafe; -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestVectorFile { - #[serde(flatten)] - tests: HashMap, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestVector { - #[allow(dead_code)] - network: String, - anchor_state: TestAnchorState, - anchor_block: TestAnchorBlock, - steps: Vec, - #[serde(rename = "_info")] - info: TestInfo, -} +/// Helper to create a genesis store for testing +fn create_genesis_store() -> Store { + let config = Config { genesis_time: 0 }; + let validators = vec![Validator::default(); 10]; + let state = State::generate_genesis_with_validators(Uint64(0), validators); -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestAnchorState { - config: TestConfig, - slot: u64, - latest_block_header: TestBlockHeader, - latest_justified: TestCheckpoint, - latest_finalized: TestCheckpoint, - #[serde(default)] - historical_block_hashes: TestDataWrapper, - #[serde(default)] - justified_slots: TestDataWrapper, - validators: TestDataWrapper, - #[serde(default)] - justifications_roots: TestDataWrapper, - #[serde(default)] - justifications_validators: TestDataWrapper, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestConfig { - genesis_time: u64, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestBlockHeader { - slot: u64, - proposer_index: u64, - parent_root: String, - state_root: String, - body_root: String, -} - -#[derive(Debug, Deserialize)] -struct TestCheckpoint { - root: String, - slot: u64, -} - -#[derive(Debug, Deserialize, Default)] -struct TestDataWrapper { - data: Vec, -} - -#[derive(Debug, Deserialize)] -struct TestValidator { - #[allow(dead_code)] - pubkey: String, - #[allow(dead_code)] - #[serde(default)] - index: u64, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestAnchorBlock { - slot: u64, - proposer_index: u64, - parent_root: String, - state_root: String, - body: TestBlockBody, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestBlock { - slot: u64, - proposer_index: u64, - parent_root: String, - state_root: String, - body: TestBlockBody, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestBlockWithAttestation { - block: TestBlock, - proposer_attestation: TestAttestation, - #[serde(default)] - block_root_label: Option, -} + let block = Block { + slot: Slot(0), + proposer_index: ValidatorIndex(0), + parent_root: Bytes32::default(), + state_root: Bytes32(state.hash_tree_root()), + body: BlockBody::default(), + }; -#[derive(Debug, Deserialize)] -struct TestBlockBody { - attestations: TestDataWrapper, -} + let signed_block = SignedBlockWithAttestation { + message: BlockWithAttestation { + block, + proposer_attestation: Default::default(), + }, + signature: Default::default(), + }; -#[derive(Debug, Deserialize)] -#[serde(untagged)] -enum TestAttestation { - #[serde(rename_all = "camelCase")] - Nested { - validator_id: u64, - data: TestAttestationData, - }, - #[serde(rename_all = "camelCase")] - Flat { - validator_id: u64, - slot: u64, - head: TestCheckpoint, - target: TestCheckpoint, - source: TestCheckpoint, - }, + get_forkchoice_store(state, signed_block, config) } -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestAttestationData { +/// Helper to create a signed attestation +fn create_attestation( + validator_id: u64, slot: u64, - head: TestCheckpoint, - target: TestCheckpoint, - source: TestCheckpoint, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestStep { - valid: bool, - #[serde(default)] - checks: Option, - #[serde(rename = "stepType")] - step_type: String, - block: Option, - attestation: Option, - tick: Option, - time: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestChecks { - #[serde(rename = "headSlot")] - head_slot: Option, - #[serde(rename = "headRootLabel")] - head_root_label: Option, - #[serde(rename = "attestationChecks")] - attestation_checks: Option>, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct AttestationCheck { - validator: u64, - #[allow(dead_code)] - #[serde(rename = "attestationSlot")] - attestation_slot: u64, - #[serde(rename = "targetSlot")] - target_slot: Option, - location: String, -} - -#[derive(Debug, Deserialize)] -struct TestInfo { - #[allow(dead_code)] - hash: String, - #[allow(dead_code)] - comment: String, - #[serde(rename = "testId")] - test_id: String, - #[allow(dead_code)] - description: String, - #[allow(dead_code)] - #[serde(rename = "fixtureFormat")] - fixture_format: String, -} - -fn parse_root(hex_str: &str) -> Bytes32 { - let hex = hex_str.trim_start_matches("0x"); - let mut bytes = [0u8; 32]; - - if hex.len() == 64 { - for i in 0..32 { - bytes[i] = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16) - .unwrap_or_else(|_| panic!("Invalid hex at position {}: {}", i, hex)); - } - } else if !hex.chars().all(|c| c == '0') { - panic!("Invalid root length: {} (expected 64 hex chars)", hex.len()); - } - - Bytes32(ssz::H256::from(bytes)) -} - -fn convert_test_checkpoint(test_cp: &TestCheckpoint) -> Checkpoint { - Checkpoint { - root: parse_root(&test_cp.root), - slot: Slot(test_cp.slot), - } -} - -fn convert_test_attestation(test_att: &TestAttestation) -> Attestation { - let (validator_id, slot, head, target, source) = match test_att { - TestAttestation::Nested { validator_id, data } => ( - *validator_id, - data.slot, - &data.head, - &data.target, - &data.source, - ), - TestAttestation::Flat { - validator_id, - slot, + head: Checkpoint, + target: Checkpoint, + source: Checkpoint, +) -> SignedAttestation { + SignedAttestation { + validator_id, + message: AttestationData { + slot: Slot(slot), head, target, source, - } => (*validator_id, *slot, head, target, source), - }; - - Attestation { - validator_id: Uint64(validator_id), - data: AttestationData { - slot: Slot(slot), - head: convert_test_checkpoint(head), - target: convert_test_checkpoint(target), - source: convert_test_checkpoint(source), }, + signature: Default::default(), } } -#[cfg(feature = "devnet1")] -fn convert_test_anchor_block(test_block: &TestAnchorBlock) -> SignedBlockWithAttestation { - let mut attestations = ssz::PersistentList::default(); - - for (i, test_att) in test_block.body.attestations.data.iter().enumerate() { - let signed_vote = convert_test_attestation(test_att); - attestations - .push(signed_vote) - .expect(&format!("Failed to add attestation {}", i)); - } - +/// Helper to add a block to the store +fn add_block(store: &mut Store, slot: u64, parent_root: Bytes32, proposer: u64) -> Bytes32 { let block = Block { - slot: Slot(test_block.slot), - proposer_index: ValidatorIndex(test_block.proposer_index), - parent_root: parse_root(&test_block.parent_root), - state_root: parse_root(&test_block.state_root), - body: BlockBody { attestations }, + slot: Slot(slot), + proposer_index: ValidatorIndex(proposer), + parent_root, + state_root: Bytes32::default(), + body: BlockBody::default(), }; - - // Create proposer attestation - let proposer_attestation = Attestation { - validator_id: Uint64(test_block.proposer_index), - data: AttestationData { - slot: Slot(test_block.slot), - head: Checkpoint { - root: parse_root(&test_block.parent_root), - slot: Slot(test_block.slot), - }, - target: Checkpoint { - root: parse_root(&test_block.parent_root), - slot: Slot(test_block.slot), - }, - source: Checkpoint { - root: parse_root(&test_block.parent_root), - slot: Slot(0), + let block_root = Bytes32(block.hash_tree_root()); + + store.blocks.insert( + block_root, + SignedBlockWithAttestation { + message: BlockWithAttestation { + block, + proposer_attestation: Default::default(), }, + signature: Default::default(), }, - }; + ); - SignedBlockWithAttestation { - message: BlockWithAttestation { - block, - proposer_attestation, - }, - signature: PersistentList::default(), - } + block_root } -#[cfg(feature = "devnet1")] -fn convert_test_block( - test_block_with_att: &TestBlockWithAttestation, -) -> SignedBlockWithAttestation { - let test_block = &test_block_with_att.block; - let mut attestations = ssz::PersistentList::default(); - - for (i, test_att) in test_block.body.attestations.data.iter().enumerate() { - let signed_vote = convert_test_attestation(test_att); - attestations - .push(signed_vote) - .expect(&format!("Failed to add attestation {}", i)); - } - - let block = Block { - slot: Slot(test_block.slot), - proposer_index: ValidatorIndex(test_block.proposer_index), - parent_root: parse_root(&test_block.parent_root), - state_root: parse_root(&test_block.state_root), - body: BlockBody { attestations }, - }; +#[test] +fn test_genesis_state_transition() { + let store = create_genesis_store(); - let proposer_attestation = convert_test_attestation(&test_block_with_att.proposer_attestation); + // Verify genesis state is properly initialized + assert!(!store.head.0.is_zero()); + assert_eq!(store.blocks.len(), 1); + assert_eq!(store.states.len(), 1); - SignedBlockWithAttestation { - message: BlockWithAttestation { - block, - proposer_attestation, - }, - signature: PersistentList::default(), - } + // Genesis should be both justified and finalized + assert_eq!(store.latest_justified.slot, Slot(0)); + assert_eq!(store.latest_finalized.slot, Slot(0)); } -fn initialize_state_from_test(test_state: &TestAnchorState) -> State { - use containers::{ - HistoricalBlockHashes, JustificationRoots, JustificationsValidators, JustifiedSlots, +#[test] +fn test_basic_slot_transition() { + let mut store = create_genesis_store(); + let genesis_root = store.head; + + // Add blocks at slots 1, 2, 3 + let block1_root = add_block(&mut store, 1, genesis_root, 0); + let block2_root = add_block(&mut store, 2, block1_root, 0); + let block3_root = add_block(&mut store, 3, block2_root, 0); + + assert_eq!(store.blocks.len(), 4); + + // Without attestations and min_votes=1, head should stay at genesis + // (no blocks have enough votes to be considered) + let empty_attestations = HashMap::new(); + let head = get_fork_choice_head(&store, genesis_root, &empty_attestations, 1); + assert_eq!(head, genesis_root); + + // With attestation for block3 and min_votes=1, head should follow the voted chain + let mut attestations = HashMap::new(); + let checkpoint = Checkpoint { + root: block3_root, + slot: Slot(3), }; - use ssz::PersistentList as List; - - let config = Config { - genesis_time: test_state.config.genesis_time, + let genesis_checkpoint = Checkpoint { + root: genesis_root, + slot: Slot(0), }; - let latest_block_header = BlockHeader { - slot: Slot(test_state.latest_block_header.slot), - proposer_index: ValidatorIndex(test_state.latest_block_header.proposer_index), - parent_root: parse_root(&test_state.latest_block_header.parent_root), - state_root: parse_root(&test_state.latest_block_header.state_root), - body_root: parse_root(&test_state.latest_block_header.body_root), - }; + attestations.insert( + ValidatorIndex(0), + create_attestation( + 0, + 3, + checkpoint.clone(), + checkpoint.clone(), + genesis_checkpoint.clone(), + ), + ); - let mut historical_block_hashes = HistoricalBlockHashes::default(); - for hash_str in &test_state.historical_block_hashes.data { - historical_block_hashes - .push(parse_root(hash_str)) - .expect("within limit"); - } + // The fork choice should follow the chain with votes to find the heaviest head + let head = get_fork_choice_head(&store, genesis_root, &attestations, 1); - let mut justified_slots = JustifiedSlots::new(false, test_state.justified_slots.data.len()); - for (i, &val) in test_state.justified_slots.data.iter().enumerate() { - if val { - justified_slots.set(i, true); - } - } + // With 1 vote on block3, the entire chain block1->block2->block3 gets 1 vote each + // So head should be block3 (the tip of the voted chain) + assert_eq!(head, block3_root); +} - let mut justifications_roots = JustificationRoots::default(); - for root_str in &test_state.justifications_roots.data { - justifications_roots - .push(parse_root(root_str)) - .expect("within limit"); - } +#[test] +fn test_attestation_processing() { + let mut store = create_genesis_store(); + let genesis_root = store.head; + let genesis_checkpoint = Checkpoint { + root: genesis_root, + slot: Slot(0), + }; - let mut justifications_validators = - JustificationsValidators::new(false, test_state.justifications_validators.data.len()); - for (i, &val) in test_state.justifications_validators.data.iter().enumerate() { - if val { - justifications_validators.set(i, true); - } - } + // Create a block + let block1_root = add_block(&mut store, 1, genesis_root, 0); + let block1_checkpoint = Checkpoint { + root: block1_root, + slot: Slot(1), + }; - let mut validators = List::default(); - for test_validator in &test_state.validators.data { - let pubkey = containers::validator::BlsPublicKey::from_hex(&test_validator.pubkey) - .expect("Failed to parse validator pubkey"); - let validator = containers::validator::Validator { - pubkey, - index: containers::Uint64(test_validator.index), - }; - validators.push(validator).expect("Failed to add validator"); + // Process attestations from multiple validators + let mut attestations = HashMap::new(); + for i in 0..5 { + attestations.insert( + ValidatorIndex(i), + create_attestation( + i, + 1, + block1_checkpoint.clone(), + block1_checkpoint.clone(), + genesis_checkpoint.clone(), + ), + ); } - State { - config, - slot: Slot(test_state.slot), - latest_block_header, - latest_justified: convert_test_checkpoint(&test_state.latest_justified), - latest_finalized: convert_test_checkpoint(&test_state.latest_finalized), - historical_block_hashes, - justified_slots, - validators, - justifications_roots, - justifications_validators, - } + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + assert_eq!(head, block1_root); } -#[cfg(feature = "devnet1")] -fn verify_checks( - store: &Store, - checks: &Option, - block_labels: &HashMap, - step_idx: usize, -) -> Result<(), String> { - // If no checks provided, nothing to verify - let checks = match checks { - Some(c) => c, - None => return Ok(()), +#[test] +fn test_multiple_attestations() { + let mut store = create_genesis_store(); + let genesis_root = store.head; + let genesis_checkpoint = Checkpoint { + root: genesis_root, + slot: Slot(0), }; - if let Some(expected_slot) = checks.head_slot { - let actual_slot = store.blocks[&store.head].message.block.slot.0; - if actual_slot != expected_slot { - return Err(format!( - "Step {}: Head slot mismatch - expected {}, got {}", - step_idx, expected_slot, actual_slot - )); - } - } + // Create a chain of blocks + let block1_root = add_block(&mut store, 1, genesis_root, 0); + let block2_root = add_block(&mut store, 2, block1_root, 0); + let block3_root = add_block(&mut store, 3, block2_root, 0); - if let Some(label) = &checks.head_root_label { - let expected_root = block_labels - .get(label) - .ok_or_else(|| format!("Step {}: Block label '{}' not found", step_idx, label))?; - if &store.head != expected_root { - let actual_slot = store - .blocks - .get(&store.head) - .map(|b| b.message.block.slot.0) - .unwrap_or(0); - let expected_slot = store - .blocks - .get(expected_root) - .map(|b| b.message.block.slot.0) - .unwrap_or(0); - return Err(format!( - "Step {}: Head root mismatch for label '{}' - expected slot {}, got slot {} (known_attestations: {}, new_attestations: {})", - step_idx, label, expected_slot, actual_slot, - store.latest_known_attestations.len(), store.latest_new_attestations.len() - )); - } - } + let block3_checkpoint = Checkpoint { + root: block3_root, + slot: Slot(3), + }; - if let Some(att_checks) = &checks.attestation_checks { - for check in att_checks { - let validator = ValidatorIndex(check.validator); - - match check.location.as_str() { - "new" => { - if !store.latest_new_attestations.contains_key(&validator) { - return Err(format!( - "Step {}: Expected validator {} in new attestations, but not found", - step_idx, check.validator - )); - } - if let Some(target_slot) = check.target_slot { - let attestation = &store.latest_new_attestations[&validator]; - if attestation.message.data.target.slot.0 != target_slot { - return Err(format!( - "Step {}: Validator {} new attestation target slot mismatch - expected {}, got {}", - step_idx, check.validator, target_slot, attestation.message.data.target.slot.0 - )); - } - } - } - "known" => { - if !store.latest_known_attestations.contains_key(&validator) { - return Err(format!( - "Step {}: Expected validator {} in known attestations, but not found", - step_idx, check.validator - )); - } - } - _ => { - return Err(format!( - "Step {}: Unknown attestation location: {}", - step_idx, check.location - )); - } - } - } + // All validators attest to block3 + let mut attestations = HashMap::new(); + for i in 0..10 { + attestations.insert( + ValidatorIndex(i), + create_attestation( + i, + 3, + block3_checkpoint.clone(), + block3_checkpoint.clone(), + genesis_checkpoint.clone(), + ), + ); } - Ok(()) + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + assert_eq!(head, block3_root); } -#[cfg(feature = "devnet1")] -fn run_single_test(_test_name: &str, test: TestVector) -> Result<(), String> { - println!(" Running: {}", test.info.test_id); +#[test] +fn test_fork_choice_with_competing_blocks() { + let mut store = create_genesis_store(); + let genesis_root = store.head; + let genesis_checkpoint = Checkpoint { + root: genesis_root, + slot: Slot(0), + }; - let mut anchor_state = initialize_state_from_test(&test.anchor_state); - let anchor_block = convert_test_anchor_block(&test.anchor_block); + // Create two competing forks at slot 1 + let fork_a_root = add_block(&mut store, 1, genesis_root, 0); + let fork_b_root = add_block(&mut store, 1, genesis_root, 1); // Different proposer - let body_root = hash_tree_root(&anchor_block.message.block.body); - anchor_state.latest_block_header = BlockHeader { - slot: anchor_block.message.block.slot, - proposer_index: anchor_block.message.block.proposer_index, - parent_root: anchor_block.message.block.parent_root, - state_root: anchor_block.message.block.state_root, - body_root, + let fork_a_checkpoint = Checkpoint { + root: fork_a_root, + slot: Slot(1), }; - - let config = Config { - genesis_time: test.anchor_state.config.genesis_time, + let fork_b_checkpoint = Checkpoint { + root: fork_b_root, + slot: Slot(1), }; - let mut store = get_forkchoice_store(anchor_state, anchor_block, config); - let mut block_labels: HashMap = HashMap::new(); - - for (step_idx, step) in test.steps.iter().enumerate() { - match step.step_type.as_str() { - "block" => { - let test_block = step - .block - .as_ref() - .ok_or_else(|| format!("Step {}: Missing block data", step_idx))?; - - let result = std::panic::catch_unwind(AssertUnwindSafe(|| { - let signed_block = convert_test_block(test_block); - let block_root = Bytes32(signed_block.message.block.hash_tree_root()); - - // Advance time to the block's slot to ensure attestations are processable - // SECONDS_PER_SLOT is 4 (not 12) - let block_time = - store.config.genesis_time + (signed_block.message.block.slot.0 * 4); - on_tick(&mut store, block_time, false); - - on_block(&mut store, signed_block)?; - Ok(block_root) - })); - - let result = match result { - Ok(inner) => inner, - Err(e) => Err(format!("Panic: {:?}", e)), - }; - - if let Ok(block_root) = &result { - if let Some(label) = &test_block.block_root_label { - block_labels.insert(label.clone(), *block_root); - } - } - - if step.valid && result.is_err() { - return Err(format!( - "Step {}: Block should be valid but processing failed: {:?}", - step_idx, - result.err() - )); - } else if !step.valid && result.is_ok() { - return Err(format!( - "Step {}: Block should be invalid but processing succeeded", - step_idx - )); - } - - if step.valid && result.is_ok() { - verify_checks(&store, &step.checks, &block_labels, step_idx)?; - } - } - "tick" | "time" => { - let time_value = step - .tick - .or(step.time) - .ok_or_else(|| format!("Step {}: Missing tick/time data", step_idx))?; - on_tick(&mut store, time_value, false); - - if step.valid { - verify_checks(&store, &step.checks, &block_labels, step_idx)?; - } - } - "attestation" => { - let test_att = step - .attestation - .as_ref() - .ok_or_else(|| format!("Step {}: Missing attestation data", step_idx))?; - - let result = std::panic::catch_unwind(AssertUnwindSafe(|| { - let attestation = convert_test_attestation(test_att); - let signed_attestation = SignedAttestation { - message: attestation, - signature: Signature::default(), - }; - on_attestation(&mut store, signed_attestation, false) - })); - - let result = match result { - Ok(inner) => inner, - Err(e) => Err(format!("Panic: {:?}", e)), - }; - - if step.valid && result.is_err() { - return Err(format!( - "Step {}: Attestation should be valid but processing failed: {:?}", - step_idx, - result.err() - )); - } else if !step.valid && result.is_ok() { - return Err(format!( - "Step {}: Attestation should be invalid but processing succeeded", - step_idx - )); - } - - if step.valid && result.is_ok() { - verify_checks(&store, &step.checks, &block_labels, step_idx)?; - } - } - _ => { - return Err(format!( - "Step {}: Unknown step type: {}", - step_idx, step.step_type - )); - } - } + // 6 validators vote for fork A + let mut attestations = HashMap::new(); + for i in 0..6 { + attestations.insert( + ValidatorIndex(i), + create_attestation( + i, + 1, + fork_a_checkpoint.clone(), + fork_a_checkpoint.clone(), + genesis_checkpoint.clone(), + ), + ); } - Ok(()) -} - -#[cfg(feature = "devnet1")] -fn run_test_vector_file(test_path: &str) -> Result<(), String> { - let json_str = std::fs::read_to_string(test_path) - .map_err(|e| format!("Failed to read file {}: {}", test_path, e))?; - - let test_data: TestVectorFile = serde_json::from_str(&json_str) - .map_err(|e| format!("Failed to parse JSON from {}: {}", test_path, e))?; - - for (test_name, test_vector) in test_data.tests { - run_single_test(&test_name, test_vector)?; + // 4 validators vote for fork B + for i in 6..10 { + attestations.insert( + ValidatorIndex(i), + create_attestation( + i, + 1, + fork_b_checkpoint.clone(), + fork_b_checkpoint.clone(), + genesis_checkpoint.clone(), + ), + ); } - Ok(()) + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + + // Fork A should win with more votes + assert_eq!(head, fork_a_root); } #[test] -#[cfg(feature = "devnet1")] -fn test_fork_choice_head_vectors() { - let test_dir = "../tests/test_vectors/test_fork_choice/test_fork_choice_head"; - - let entries = - std::fs::read_dir(test_dir).expect(&format!("Failed to read test directory: {}", test_dir)); - - let mut test_count = 0; - let mut pass_count = 0; - let mut fail_count = 0; - - println!("\n=== Fork Choice Head Tests ==="); - - for entry in entries { - let path = entry.unwrap().path(); - if path.extension().map_or(false, |ext| ext == "json") { - test_count += 1; - println!("\nTest file: {:?}", path.file_name().unwrap()); - - match run_test_vector_file(path.to_str().unwrap()) { - Ok(_) => { - println!(" ✓ PASSED"); - pass_count += 1; - } - Err(e) => { - println!(" ✗ FAILED: {}", e); - fail_count += 1; - } - } - } - } +fn test_finality_prevents_reorg() { + let mut store = create_genesis_store(); + let genesis_root = store.head; + let genesis_checkpoint = Checkpoint { + root: genesis_root, + slot: Slot(0), + }; - println!("\n=== Summary ==="); - println!( - "Total: {}, Passed: {}, Failed: {}", - test_count, pass_count, fail_count - ); + // Create a finalized chain + let block1_root = add_block(&mut store, 1, genesis_root, 0); + let block2_root = add_block(&mut store, 2, block1_root, 0); - if fail_count > 0 { - panic!("{} test(s) failed", fail_count); - } -} + // Update finalized checkpoint + store.latest_finalized = Checkpoint { + root: block1_root, + slot: Slot(1), + }; -#[test] -#[cfg(feature = "devnet1")] -fn test_attestation_processing_vectors() { - let test_dir = "../tests/test_vectors/test_fork_choice/test_attestation_processing"; - - let entries = - std::fs::read_dir(test_dir).expect(&format!("Failed to read test directory: {}", test_dir)); - - let mut test_count = 0; - let mut pass_count = 0; - let mut fail_count = 0; - - println!("\n=== Attestation Processing Tests ==="); - - for entry in entries { - let path = entry.unwrap().path(); - if path.extension().map_or(false, |ext| ext == "json") { - test_count += 1; - println!("\nTest file: {:?}", path.file_name().unwrap()); - - match run_test_vector_file(path.to_str().unwrap()) { - Ok(_) => { - println!(" ✓ PASSED"); - pass_count += 1; - } - Err(e) => { - println!(" ✗ FAILED: {}", e); - fail_count += 1; - } - } - } - } + // Create competing fork from genesis (should not be chosen due to finality) + let competing_root = add_block(&mut store, 1, genesis_root, 1); - println!("\n=== Summary ==="); - println!( - "Total: {}, Passed: {}, Failed: {}", - test_count, pass_count, fail_count - ); + let block2_checkpoint = Checkpoint { + root: block2_root, + slot: Slot(2), + }; + let competing_checkpoint = Checkpoint { + root: competing_root, + slot: Slot(1), + }; - if fail_count > 0 { - panic!("{} test(s) failed", fail_count); + // More votes for competing fork + let mut attestations = HashMap::new(); + for i in 0..7 { + attestations.insert( + ValidatorIndex(i), + create_attestation( + i, + 1, + competing_checkpoint.clone(), + competing_checkpoint.clone(), + genesis_checkpoint.clone(), + ), + ); } -} - -#[test] -#[cfg(feature = "devnet1")] -fn test_fork_choice_reorgs_vectors() { - let test_dir = "../tests/test_vectors/test_fork_choice/test_fork_choice_reorgs"; - - let entries = - std::fs::read_dir(test_dir).expect(&format!("Failed to read test directory: {}", test_dir)); - - let mut test_count = 0; - let mut pass_count = 0; - let mut fail_count = 0; - - println!("\n=== Fork Choice Reorg Tests ==="); - - for entry in entries { - let path = entry.unwrap().path(); - if path.extension().map_or(false, |ext| ext == "json") { - test_count += 1; - println!("\nTest file: {:?}", path.file_name().unwrap()); - - match run_test_vector_file(path.to_str().unwrap()) { - Ok(_) => { - println!(" ✓ PASSED"); - pass_count += 1; - } - Err(e) => { - println!(" ✗ FAILED: {}", e); - fail_count += 1; - } - } - } + for i in 7..10 { + attestations.insert( + ValidatorIndex(i), + create_attestation( + i, + 2, + block2_checkpoint.clone(), + block2_checkpoint.clone(), + genesis_checkpoint.clone(), + ), + ); } - println!("\n=== Summary ==="); - println!( - "Total: {}, Passed: {}, Failed: {}", - test_count, pass_count, fail_count - ); + // Start from finalized block1 + let head = get_fork_choice_head(&store, block1_root, &attestations, 0); - if fail_count > 0 { - panic!("{} test(s) failed", fail_count); - } + // Should follow the chain from block1, not competing fork + assert_eq!(head, block2_root); } #[test] -#[cfg(feature = "devnet1")] -fn test_attestation_target_selection_vectors() { - let test_dir = "../tests/test_vectors/test_fork_choice/test_attestation_target_selection"; - - let entries = - std::fs::read_dir(test_dir).expect(&format!("Failed to read test directory: {}", test_dir)); - - let mut test_count = 0; - let mut pass_count = 0; - let mut fail_count = 0; - - println!("\n=== Attestation Target Selection Tests ==="); - - for entry in entries { - let path = entry.unwrap().path(); - if path.extension().map_or(false, |ext| ext == "json") { - test_count += 1; - println!("\nTest file: {:?}", path.file_name().unwrap()); - - match run_test_vector_file(path.to_str().unwrap()) { - Ok(_) => { - println!(" ✓ PASSED"); - pass_count += 1; - } - Err(e) => { - println!(" ✗ FAILED: {}", e); - fail_count += 1; - } - } - } - } +fn test_attestation_from_future_slot() { + let mut store = create_genesis_store(); + let genesis_root = store.head; + let genesis_checkpoint = Checkpoint { + root: genesis_root, + slot: Slot(0), + }; - println!("\n=== Summary ==="); - println!( - "Total: {}, Passed: {}, Failed: {}", - test_count, pass_count, fail_count + // Create block at slot 1 + let block1_root = add_block(&mut store, 1, genesis_root, 0); + let block1_checkpoint = Checkpoint { + root: block1_root, + slot: Slot(1), + }; + + // Attestation claims to be from slot 100 (future) + // The fork choice still processes it based on what block it points to + let mut attestations = HashMap::new(); + attestations.insert( + ValidatorIndex(0), + create_attestation( + 0, + 100, + block1_checkpoint.clone(), + block1_checkpoint.clone(), + genesis_checkpoint.clone(), + ), ); - if fail_count > 0 { - panic!("{} test(s) failed", fail_count); - } + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + + // Should still follow the attestation to block1 + assert_eq!(head, block1_root); } #[test] -#[cfg(feature = "devnet1")] -fn test_lexicographic_tiebreaker_vectors() { - let test_dir = "../tests/test_vectors/test_fork_choice/test_lexicographic_tiebreaker"; - - let entries = - std::fs::read_dir(test_dir).expect(&format!("Failed to read test directory: {}", test_dir)); - - let mut test_count = 0; - let mut pass_count = 0; - let mut fail_count = 0; - - println!("\n=== Lexicographic Tiebreaker Tests ==="); - - for entry in entries { - let path = entry.unwrap().path(); - if path.extension().map_or(false, |ext| ext == "json") { - test_count += 1; - println!("\nTest file: {:?}", path.file_name().unwrap()); - - match run_test_vector_file(path.to_str().unwrap()) { - Ok(_) => { - println!(" ✓ PASSED"); - pass_count += 1; - } - Err(e) => { - println!(" ✗ FAILED: {}", e); - fail_count += 1; - } - } - } - } +fn test_empty_attestations_returns_root() { + let store = create_genesis_store(); + let genesis_root = store.head; - println!("\n=== Summary ==="); - println!( - "Total: {}, Passed: {}, Failed: {}", - test_count, pass_count, fail_count - ); + let empty_attestations = HashMap::new(); + let head = get_fork_choice_head(&store, genesis_root, &empty_attestations, 0); - if fail_count > 0 { - panic!("{} test(s) failed", fail_count); - } + // With no attestations, should return the provided root + assert_eq!(head, genesis_root); } diff --git a/lean_client/fork_choice/tests/unit_tests/votes.rs b/lean_client/fork_choice/tests/unit_tests/votes.rs index d6c2ad4..a3c9b8a 100644 --- a/lean_client/fork_choice/tests/unit_tests/votes.rs +++ b/lean_client/fork_choice/tests/unit_tests/votes.rs @@ -1,315 +1,258 @@ +//! Vote/attestation unit tests for devnet2 +//! +//! Tests for vote processing and fork choice weight calculations +//! using the devnet2 SignedAttestation structure. + use super::common::create_test_store; use containers::{ - attestation::{Attestation, AttestationData, Signature, SignedAttestation}, + attestation::{AttestationData, SignedAttestation}, + block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, checkpoint::Checkpoint, - Bytes32, Slot, Uint64, ValidatorIndex, + Bytes32, Slot, ValidatorIndex, }; -use fork_choice::handlers::on_attestation; -use fork_choice::store::{accept_new_attestations, INTERVALS_PER_SLOT}; +use fork_choice::store::get_fork_choice_head; +use ssz::SszHash; +use std::collections::HashMap; -#[cfg(feature = "devnet1")] +/// Helper to create a SignedAttestation for devnet2 fn create_signed_attestation( validator_id: u64, - slot: Slot, + slot: u64, head_root: Bytes32, + head_slot: u64, + target_root: Bytes32, + target_slot: u64, + source_root: Bytes32, + source_slot: u64, ) -> SignedAttestation { SignedAttestation { - message: Attestation { - validator_id: Uint64(validator_id), - data: AttestationData { - slot, - head: Checkpoint { - root: head_root, - slot, - }, - target: Checkpoint { - root: head_root, - slot, - }, - source: Checkpoint { - root: Bytes32::default(), - slot: Slot(0), - }, + validator_id, + message: AttestationData { + slot: Slot(slot), + head: Checkpoint { + root: head_root, + slot: Slot(head_slot), + }, + target: Checkpoint { + root: target_root, + slot: Slot(target_slot), + }, + source: Checkpoint { + root: source_root, + slot: Slot(source_slot), }, }, - signature: Signature::default(), + signature: Default::default(), } } #[test] -#[cfg(feature = "devnet1")] -fn test_accept_new_attestations() { - let mut store = create_test_store(); - - // Setup initial known attestations - let val1 = ValidatorIndex(1); - let val2 = ValidatorIndex(2); - let val3 = ValidatorIndex(3); - - store - .latest_known_attestations - .insert(val1, create_signed_attestation(1, Slot(0), store.head)); - - // Val1 updates their attestation to Slot 1 - store - .latest_new_attestations - .insert(val1, create_signed_attestation(1, Slot(1), store.head)); - // Val2 casts a new attestation for Slot 1 - store - .latest_new_attestations - .insert(val2, create_signed_attestation(2, Slot(1), store.head)); - // Val3 casts a new attestation for Slot 2 - store - .latest_new_attestations - .insert(val3, create_signed_attestation(3, Slot(2), store.head)); - - accept_new_attestations(&mut store); - - assert_eq!(store.latest_new_attestations.len(), 0); - assert_eq!(store.latest_known_attestations.len(), 3); - - assert_eq!( - store.latest_known_attestations[&val1].message.data.slot, - Slot(1) - ); - assert_eq!( - store.latest_known_attestations[&val2].message.data.slot, - Slot(1) +fn test_single_vote_updates_head() { + let store = create_test_store(); + let genesis_root = store.head; + + // Create attestation pointing to genesis + let attestation = create_signed_attestation( + 0, // validator_id + 1, // slot + genesis_root, // head_root + 0, // head_slot + genesis_root, // target_root + 0, // target_slot + genesis_root, // source_root + 0, // source_slot ); - assert_eq!( - store.latest_known_attestations[&val3].message.data.slot, - Slot(2) - ); -} -#[test] -#[cfg(feature = "devnet1")] -fn test_accept_new_attestations_multiple() { - let mut store = create_test_store(); - - for i in 0..5 { - store.latest_new_attestations.insert( - ValidatorIndex(i), - create_signed_attestation(i, Slot(i), store.head), - ); - } + let mut attestations = HashMap::new(); + attestations.insert(ValidatorIndex(0), attestation); - assert_eq!(store.latest_new_attestations.len(), 5); - assert_eq!(store.latest_known_attestations.len(), 0); + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); - accept_new_attestations(&mut store); - - assert_eq!(store.latest_new_attestations.len(), 0); - assert_eq!(store.latest_known_attestations.len(), 5); + // With only one block, head should still be genesis + assert_eq!(head, genesis_root); } #[test] -fn test_accept_new_attestations_empty() { - let mut store = create_test_store(); - let initial_known = store.latest_known_attestations.len(); +fn test_multiple_votes_same_block() { + let store = create_test_store(); + let genesis_root = store.head; - accept_new_attestations(&mut store); + // Multiple validators vote for same block + let mut attestations = HashMap::new(); + for i in 0..5 { + let attestation = + create_signed_attestation(i, 1, genesis_root, 0, genesis_root, 0, genesis_root, 0); + attestations.insert(ValidatorIndex(i), attestation); + } - assert_eq!(store.latest_new_attestations.len(), 0); - assert_eq!(store.latest_known_attestations.len(), initial_known); + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + + // All votes on same block, head unchanged + assert_eq!(head, genesis_root); } #[test] -#[cfg(feature = "devnet1")] -fn test_on_attestation_lifecycle() { +fn test_competing_votes_different_blocks() { let mut store = create_test_store(); - let validator_idx = ValidatorIndex(1); - let slot_0 = Slot(0); - let slot_1 = Slot(1); - - // 1. Attestation from network (gossip) - let signed_attestation_gossip = create_signed_attestation(1, slot_0, store.head); - - on_attestation(&mut store, signed_attestation_gossip.clone(), false) - .expect("Gossip attestation valid"); - - // Should be in new_attestations, not known_attestations - assert!(store.latest_new_attestations.contains_key(&validator_idx)); - assert!(!store.latest_known_attestations.contains_key(&validator_idx)); - assert_eq!( - store.latest_new_attestations[&validator_idx] - .message - .data - .slot, - slot_0 - ); - - // 2. Same attestation included in a block - on_attestation(&mut store, signed_attestation_gossip, true).expect("Block attestation valid"); - - assert!(store.latest_known_attestations.contains_key(&validator_idx)); - assert_eq!( - store.latest_known_attestations[&validator_idx] - .message - .data - .slot, - slot_0 + let genesis_root = store.head; + + // Create two competing blocks at slot 1 + let block_a = Block { + slot: Slot(1), + proposer_index: ValidatorIndex(0), + parent_root: genesis_root, + state_root: Bytes32::default(), + body: BlockBody::default(), + }; + let block_a_root = Bytes32(block_a.hash_tree_root()); + + let mut block_b = block_a.clone(); + block_b.proposer_index = ValidatorIndex(1); // Different proposer to get different root + let block_b_root = Bytes32(block_b.hash_tree_root()); + + store.blocks.insert( + block_a_root, + SignedBlockWithAttestation { + message: BlockWithAttestation { + block: block_a, + proposer_attestation: Default::default(), + }, + signature: Default::default(), + }, ); - // 3. Newer attestation from network - store.time = 1 * INTERVALS_PER_SLOT; // Advance time - let signed_attestation_next = create_signed_attestation(1, slot_1, store.head); - - on_attestation(&mut store, signed_attestation_next, false) - .expect("Next gossip attestation valid"); - - // Should update new_attestations - assert_eq!( - store.latest_new_attestations[&validator_idx] - .message - .data - .slot, - slot_1 - ); - // Known attestations should still be at slot 0 until accepted - assert_eq!( - store.latest_known_attestations[&validator_idx] - .message - .data - .slot, - slot_0 + store.blocks.insert( + block_b_root, + SignedBlockWithAttestation { + message: BlockWithAttestation { + block: block_b, + proposer_attestation: Default::default(), + }, + signature: Default::default(), + }, ); -} -#[test] -#[cfg(feature = "devnet1")] -fn test_on_attestation_future_slot() { - let mut store = create_test_store(); - let future_slot = Slot(100); // Far in the future + // 3 votes for block_a, 2 votes for block_b + let mut attestations = HashMap::new(); + for i in 0..3 { + attestations.insert( + ValidatorIndex(i), + create_signed_attestation(i, 1, block_a_root, 1, genesis_root, 0, genesis_root, 0), + ); + } + for i in 3..5 { + attestations.insert( + ValidatorIndex(i), + create_signed_attestation(i, 1, block_b_root, 1, genesis_root, 0, genesis_root, 0), + ); + } - let signed_attestation = create_signed_attestation(1, future_slot, store.head); + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); - let result = on_attestation(&mut store, signed_attestation, false); - assert!(result.is_err()); + // Block A should win with more votes + assert_eq!(head, block_a_root); } #[test] -#[cfg(feature = "devnet1")] -fn test_on_attestation_update_vote() { +fn test_vote_weight_accumulation() { let mut store = create_test_store(); - let validator_idx = ValidatorIndex(1); - - // First attestation at slot 0 - let signed_attestation1 = create_signed_attestation(1, Slot(0), store.head); - - on_attestation(&mut store, signed_attestation1, false).expect("First attestation valid"); - assert_eq!( - store.latest_new_attestations[&validator_idx] - .message - .data - .slot, - Slot(0) + let genesis_root = store.head; + + // Create a chain: genesis -> block1 -> block2 + let block1 = Block { + slot: Slot(1), + proposer_index: ValidatorIndex(0), + parent_root: genesis_root, + state_root: Bytes32::default(), + body: BlockBody::default(), + }; + let block1_root = Bytes32(block1.hash_tree_root()); + + let block2 = Block { + slot: Slot(2), + proposer_index: ValidatorIndex(0), + parent_root: block1_root, + state_root: Bytes32::default(), + body: BlockBody::default(), + }; + let block2_root = Bytes32(block2.hash_tree_root()); + + store.blocks.insert( + block1_root, + SignedBlockWithAttestation { + message: BlockWithAttestation { + block: block1, + proposer_attestation: Default::default(), + }, + signature: Default::default(), + }, + ); + store.blocks.insert( + block2_root, + SignedBlockWithAttestation { + message: BlockWithAttestation { + block: block2, + proposer_attestation: Default::default(), + }, + signature: Default::default(), + }, ); - // Advance time to allow slot 1 - store.time = 1 * INTERVALS_PER_SLOT; + // Vote for block2 - should accumulate to block1 as well + let mut attestations = HashMap::new(); + attestations.insert( + ValidatorIndex(0), + create_signed_attestation(0, 2, block2_root, 2, genesis_root, 0, genesis_root, 0), + ); - // Second attestation at slot 1 - let signed_attestation2 = create_signed_attestation(1, Slot(1), store.head); + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); - on_attestation(&mut store, signed_attestation2, false).expect("Second attestation valid"); - assert_eq!( - store.latest_new_attestations[&validator_idx] - .message - .data - .slot, - Slot(1) - ); + // Head should be block2 (the one with votes) + assert_eq!(head, block2_root); } #[test] -#[cfg(feature = "devnet1")] -fn test_on_attestation_ignore_old_vote() { - let mut store = create_test_store(); - let validator_idx = ValidatorIndex(1); +fn test_duplicate_vote_uses_latest() { + let store = create_test_store(); + let genesis_root = store.head; - // Advance time - store.time = 2 * INTERVALS_PER_SLOT; + // Same validator can only have one vote in the map (latest wins) + let mut attestations = HashMap::new(); - // Newer attestation first - let signed_attestation_new = create_signed_attestation(1, Slot(2), store.head); - - on_attestation(&mut store, signed_attestation_new, false).expect("New attestation valid"); - assert_eq!( - store.latest_new_attestations[&validator_idx] - .message - .data - .slot, - Slot(2) + // Insert a vote + attestations.insert( + ValidatorIndex(0), + create_signed_attestation(0, 1, genesis_root, 0, genesis_root, 0, genesis_root, 0), ); - // Older attestation second - let signed_attestation_old = create_signed_attestation(1, Slot(1), store.head); - - on_attestation(&mut store, signed_attestation_old, false) - .expect("Old attestation processed but ignored"); - // Should still be slot 2 - assert_eq!( - store.latest_new_attestations[&validator_idx] - .message - .data - .slot, - Slot(2) + // "Update" with same validator - only latest is kept + attestations.insert( + ValidatorIndex(0), + create_signed_attestation(0, 2, genesis_root, 0, genesis_root, 0, genesis_root, 0), ); -} - -#[test] -#[cfg(feature = "devnet1")] -fn test_on_attestation_from_block_supersedes_new() { - let mut store = create_test_store(); - let validator_idx = ValidatorIndex(1); - - // First, add attestation via gossip - let signed_attestation1 = create_signed_attestation(1, Slot(0), store.head); - on_attestation(&mut store, signed_attestation1, false).expect("Gossip attestation valid"); - - assert!(store.latest_new_attestations.contains_key(&validator_idx)); - assert!(!store.latest_known_attestations.contains_key(&validator_idx)); - // Then, add same attestation via block (on-chain) - let signed_attestation2 = create_signed_attestation(1, Slot(0), store.head); - on_attestation(&mut store, signed_attestation2, true).expect("Block attestation valid"); + // Should only have 1 attestation + assert_eq!(attestations.len(), 1); - // Should move from new to known - assert!(!store.latest_new_attestations.contains_key(&validator_idx)); - assert!(store.latest_known_attestations.contains_key(&validator_idx)); + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + assert_eq!(head, genesis_root); } #[test] -#[cfg(feature = "devnet1")] -fn test_on_attestation_newer_from_block_removes_older_new() { - let mut store = create_test_store(); - let validator_idx = ValidatorIndex(1); - - // Add older attestation via gossip - let signed_attestation_gossip = create_signed_attestation(1, Slot(0), store.head); - on_attestation(&mut store, signed_attestation_gossip, false).expect("Gossip attestation valid"); - - assert_eq!( - store.latest_new_attestations[&validator_idx] - .message - .data - .slot, - Slot(0) +fn test_vote_for_unknown_block_ignored() { + let store = create_test_store(); + let genesis_root = store.head; + let unknown_root = Bytes32(ssz::H256::from_slice(&[0xff; 32])); + + // Vote for block that doesn't exist + let mut attestations = HashMap::new(); + attestations.insert( + ValidatorIndex(0), + create_signed_attestation(0, 1, unknown_root, 1, genesis_root, 0, genesis_root, 0), ); - // Add newer attestation via block (on-chain) - store.time = 1 * INTERVALS_PER_SLOT; - let signed_attestation_block = create_signed_attestation(1, Slot(1), store.head); - on_attestation(&mut store, signed_attestation_block, true).expect("Block attestation valid"); - - // New attestation should be removed (superseded by newer on-chain one) - assert!(!store.latest_new_attestations.contains_key(&validator_idx)); - assert_eq!( - store.latest_known_attestations[&validator_idx] - .message - .data - .slot, - Slot(1) - ); + let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + + // Should still return genesis since unknown block is skipped + assert_eq!(head, genesis_root); } diff --git a/lean_client/networking/Cargo.toml b/lean_client/networking/Cargo.toml index 9025e53..a45e3a9 100644 --- a/lean_client/networking/Cargo.toml +++ b/lean_client/networking/Cargo.toml @@ -5,8 +5,6 @@ edition = "2024" [features] default = [] -devnet1 = ["containers/devnet1", "env-config/devnet1"] -devnet2 = ["containers/devnet2", "env-config/devnet1"] [dependencies] env-config = { path = "../env-config", default-features = false } diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index a78bd93..1c97353 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -373,9 +373,6 @@ where } } Ok(GossipsubMessage::Attestation(signed_attestation)) => { - #[cfg(feature = "devnet1")] - let slot = signed_attestation.message.data.slot.0; - #[cfg(feature = "devnet2")] let slot = signed_attestation.message.slot.0; if let Err(err) = self @@ -598,9 +595,6 @@ where } } OutboundP2pRequest::GossipAttestation(signed_attestation) => { - #[cfg(feature = "devnet1")] - let slot = signed_attestation.message.data.slot.0; - #[cfg(feature = "devnet2")] let slot = signed_attestation.message.slot.0; match signed_attestation.to_ssz() { diff --git a/lean_client/networking/src/types.rs b/lean_client/networking/src/types.rs index bbe7cba..124ca5a 100644 --- a/lean_client/networking/src/types.rs +++ b/lean_client/networking/src/types.rs @@ -102,17 +102,6 @@ impl Display for ChainMessage { signed_block_with_attestation.message.block.slot.0 ) } - #[cfg(feature = "devnet1")] - ChainMessage::ProcessAttestation { - signed_attestation, .. - } => { - write!( - f, - "ProcessAttestation(slot={})", - signed_attestation.message.data.slot.0 - ) - } - #[cfg(feature = "devnet2")] ChainMessage::ProcessAttestation { signed_attestation, .. } => { diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index 002beca..61a4d14 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -158,7 +158,7 @@ async fn main() { .iter() .enumerate() .map(|(i, v_str)| { - let pubkey = containers::validator::BlsPublicKey::from_hex(v_str) + let pubkey = containers::public_key::PublicKey::from_hex(v_str) .expect("Invalid genesis validator pubkey"); containers::validator::Validator { pubkey, @@ -172,7 +172,7 @@ async fn main() { let num_validators = 3; let validators = (0..num_validators) .map(|i| containers::validator::Validator { - pubkey: containers::validator::BlsPublicKey::default(), + pubkey: containers::public_key::PublicKey::default(), index: Uint64(i as u64), }) .collect(); @@ -214,9 +214,6 @@ async fn main() { block: genesis_block, proposer_attestation: genesis_proposer_attestation, }, - #[cfg(feature = "devnet1")] - signature: PersistentList::default(), - #[cfg(feature = "devnet2")] signature: BlockSignatures { attestation_signatures: PersistentList::default(), proposer_signature: Signature::default(), @@ -428,9 +425,6 @@ async fn main() { if last_attestation_slot != Some(current_slot) { let attestations = vs.create_attestations(&store, Slot(current_slot)); for signed_att in attestations { - #[cfg(feature = "devnet1")] - let validator_id = signed_att.message.validator_id.0; - #[cfg(feature = "devnet2")] let validator_id = signed_att.validator_id; info!( slot = current_slot, @@ -438,19 +432,6 @@ async fn main() { "Broadcasting attestation" ); - #[cfg(feature = "devnet1")] - match on_attestation(&mut store, signed_att.clone(), false) { - Ok(()) => { - if let Err(e) = chain_outbound_sender.send( - OutboundP2pRequest::GossipAttestation(signed_att) - ) { - warn!("Failed to gossip attestation: {}", e); - } - } - Err(e) => warn!("Error processing own attestation: {}", e), - } - - #[cfg(feature = "devnet2")] match on_attestation(&mut store, signed_att.clone(), false) { Ok(()) => { if let Err(e) = chain_outbound_sender.send( @@ -546,22 +527,9 @@ async fn main() { should_gossip, .. } => { - #[cfg(feature = "devnet1")] - let att_slot = signed_attestation.message.data.slot.0; - #[cfg(feature = "devnet1")] - let source_slot = signed_attestation.message.data.source.slot.0; - #[cfg(feature = "devnet1")] - let target_slot = signed_attestation.message.data.target.slot.0; - #[cfg(feature = "devnet1")] - let validator_id = signed_attestation.message.validator_id.0; - - #[cfg(feature = "devnet2")] let att_slot = signed_attestation.message.slot.0; - #[cfg(feature = "devnet2")] let source_slot = signed_attestation.message.source.slot.0; - #[cfg(feature = "devnet2")] let target_slot = signed_attestation.message.target.slot.0; - #[cfg(feature = "devnet2")] let validator_id = signed_attestation.validator_id; info!( diff --git a/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json b/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json index a4cd2af..ef3fcf3 100644 --- a/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json +++ b/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json @@ -1,6 +1,7 @@ { "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet][fork_Devnet-verify_signatures_test]": { "network": "Devnet", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -30,7 +31,7 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 } ] @@ -47,8 +48,8 @@ "block": { "slot": 1, "proposerIndex": 0, - "parentRoot": "0x7ee509d36952a8f41f5dc5b4627487f4d523c9333ef7af2a692ae12867eeee16", - "stateRoot": "0xe9dd253c1cff8e8719113e66970f54a44c885997f3e57ba6dc993861eca4f40c", + "parentRoot": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", + "stateRoot": "0x38d7edaf9c08ffb285eb3e1e64456fbe430d4c4a4bab9b0bf29565b74da5c620", "body": { "attestations": { "data": [] @@ -60,52 +61,53 @@ "data": { "slot": 1, "head": { - "root": "0xf0c95e7f668652482cc00916c57ab6c84b435a29d030f682b92d95f9f3add62a", + "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", "slot": 1 }, "target": { - "root": "0xf0c95e7f668652482cc00916c57ab6c84b435a29d030f682b92d95f9f3add62a", + "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", "slot": 1 }, "source": { - "root": "0x7ee509d36952a8f41f5dc5b4627487f4d523c9333ef7af2a692ae12867eeee16", + "root": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", "slot": 0 } } } }, "signature": { - "data": [ - { - "path": { - "siblings": { - "data": [] - } - }, - "rho": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - }, - "hashes": { + "attestationSignatures": { + "data": [] + }, + "proposerSignature": { + "path": { + "siblings": { "data": [] } + }, + "rho": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + "hashes": { + "data": [] } - ] + } } }, "expectException": "AssertionError", "_info": { - "hash": "0x7befd11b0a26c4bfbd4c40d5b4c21d7a510cc2ed899aa3f494b7c4d9574fd7d3", + "hash": "0x8d97e6b6a601e10856ae70720ffad8cd392dab8169fe158054edac0bc0f0e49a", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet]", - "description": "Test that invalid signatures are properly rejected during verification.\n\n Scenario\n --------\n - Single block at slot 1\n - Proposer attestation has an invalid signature\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation is rejected\n\n Why This Matters\n ----------------\n This test verifies the negative case:\n - Signature verification actually validates cryptographic correctness\n not just structural correctness.\n - Invalid signatures are caught, not silently accepted", + "description": "Test that invalid signatures are properly rejected during verification.\n\nScenario\n--------\n- Single block at slot 1\n- Proposer attestation has an invalid signature\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation is rejected\n\nWhy This Matters\n----------------\nThis test verifies the negative case:\n- Signature verification actually validates cryptographic correctness\n not just structural correctness.\n- Invalid signatures are caught, not silently accepted", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json b/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json index 306a4a2..ad8df6f 100644 --- a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json +++ b/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json @@ -1,6 +1,7 @@ { "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet][fork_Devnet-verify_signatures_test]": { "network": "Devnet", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -30,15 +31,15 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 } ] @@ -55,31 +56,19 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0x9e3b89451933da29e3697c588770a4d63c900e0a8af56e2a4e0777abdd355450", - "stateRoot": "0x85222dc92460f8b51fe414fb34ef9f35247653eb6036b1268261a91ba617cda8", + "parentRoot": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", + "stateRoot": "0x92f8e374cbfbb7a91bee6a58e0c60cb6781bc4ba4bea028be0e46e5146b9e034", "body": { "attestations": { "data": [ { - "validatorId": 0, - "data": { - "slot": 1, - "head": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "target": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "source": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - } - } - }, - { - "validatorId": 2, + "aggregationBits": { + "data": [ + true, + false, + true + ] + }, "data": { "slot": 1, "head": { @@ -105,531 +94,1219 @@ "data": { "slot": 1, "head": { - "root": "0xde28efe59b19913717bb76a32611fe839ad69a4b3c0915d79c8304b35b57ddf7", + "root": "0xc18aff973b62478f234db568edc4811cd2a59885fa7f712e593b9956dde7fa5a", "slot": 1 }, "target": { - "root": "0xde28efe59b19913717bb76a32611fe839ad69a4b3c0915d79c8304b35b57ddf7", + "root": "0xc18aff973b62478f234db568edc4811cd2a59885fa7f712e593b9956dde7fa5a", "slot": 1 }, "source": { - "root": "0x9e3b89451933da29e3697c588770a4d63c900e0a8af56e2a4e0777abdd355450", + "root": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", "slot": 0 } } } }, "signature": { - "data": [ - { - "path": { - "siblings": { + "attestationSignatures": { + "data": [ + { + "participants": { "data": [ - { - "data": [ - 1715018400, - 48710923, - 1748245036, - 163403131, - 924640484, - 1566519705, - 1860210712, - 1236746232 - ] - }, - { - "data": [ - 318044943, - 399009750, - 1038257959, - 729848679, - 1449298444, - 436326364, - 977163460, - 1497861895 - ] - }, - { - "data": [ - 1468833114, - 1734637349, - 1929839981, - 1267639175, - 796117685, - 47500478, - 1956344905, - 1320986094 - ] - }, - { - "data": [ - 1266260145, - 725202725, - 218929017, - 126358625, - 921715766, - 1979527002, - 1695252564, - 1220353106 - ] - }, - { - "data": [ - 298307958, - 2042817198, - 1699263182, - 1453266496, - 1023068754, - 224889272, - 2049483392, - 1399154486 - ] - }, - { - "data": [ - 1430973325, - 1579483336, - 1154958176, - 318946268, - 1584562777, - 1947187050, - 886182999, - 1154818886 - ] - }, - { - "data": [ - 1056622773, - 601147086, - 1222204938, - 264848405, - 1363314459, - 109131915, - 517301456, - 938514082 - ] - }, - { - "data": [ - 483358618, - 57732057, - 329296853, - 1352276692, - 88248225, - 1800662461, - 2098999624, - 2064134886 - ] - } + true, + false, + true ] + }, + "proofData": { + "data": "0x00" } - }, - "rho": { - "data": [ - 1133244814, - 826948562, - 694211053, - 360187930, - 342494093, - 526958919, - 549074822 - ] - }, - "hashes": { + } + ] + }, + "proposerSignature": { + "path": { + "siblings": { "data": [ { "data": [ - 780469602, - 1390643088, - 2027017214, - 1431163446, - 1529907007, - 365863100, - 1195111668, - 1880854250 + 1291233891, + 901611691, + 1515447763, + 518859210, + 691456689, + 1723616255, + 1740655893, + 1397350123 ] }, { "data": [ - 1605769376, - 741812234, - 1163184354, - 1147446555, - 871882010, - 948907942, - 551347671, - 840722750 + 1456518562, + 484811598, + 2010092021, + 282492124, + 1770180907, + 1769233778, + 494579282, + 1699827616 ] }, { "data": [ - 1181134299, - 1236421381, - 185118722, - 573142269, - 160921481, - 1510683126, - 294606954, - 1927123925 + 704838371, + 2081776305, + 559183133, + 1733361779, + 263725425, + 344472513, + 2086779080, + 1240527530 ] }, { "data": [ - 1347741188, - 1460449909, - 596275218, - 1289700342, - 1411024602, - 1833568587, - 1711725928, - 6783578 + 1039415328, + 308461932, + 2043838459, + 611597666, + 1580071319, + 1516124162, + 698977291, + 1585930624 ] - } - ] - } - }, - { - "path": { - "siblings": { - "data": [ - { - "data": [ - 245987955, - 1574460560, - 1525707632, - 123960916, - 1086053397, - 2080663024, - 366379460, - 1998029054 - ] - }, - { - "data": [ - 2019630499, - 1360353298, - 1175664713, - 1796753426, - 1565903239, - 1735318974, - 1982905657, - 614050054 - ] - }, - { - "data": [ - 1454884048, - 454543812, - 1015264795, - 356242932, - 501670992, - 463708403, - 459704849, - 1579831167 - ] - }, - { - "data": [ - 665199378, - 1891601081, - 858563106, - 596194897, - 332003681, - 2038255694, - 1339684636, - 1387152804 - ] - }, - { - "data": [ - 543025152, - 786491463, - 1123029687, - 1264918908, - 984563024, - 1188331300, - 680922823, - 1816896828 - ] - }, - { - "data": [ - 1326664843, - 1319602875, - 1528705430, - 227865620, - 708371624, - 1637263589, - 1037139620, - 1485301417 - ] - }, - { - "data": [ - 1192630398, - 484414116, - 313965305, - 196703586, - 1501710664, - 1683049995, - 904524380, - 1174797428 - ] - }, - { - "data": [ - 2063074693, - 101123966, - 607293596, - 477669941, - 1941778158, - 1668669356, - 1179339209, - 864743993 - ] - } - ] - } - }, - "rho": { - "data": [ - 2045446833, - 446323957, - 1182968240, - 517145103, - 1767538833, - 631690441, - 1778446117 - ] - }, - "hashes": { - "data": [ + }, { "data": [ - 423057391, - 388700883, - 1083440537, - 359923932, - 388595029, - 1736053252, - 350530532, - 731868998 + 250662127, + 1008190943, + 983708486, + 1247986374, + 1886580775, + 1647509743, + 1550488627, + 1260597451 ] }, { "data": [ - 1090832151, - 533474659, - 809763430, - 507768385, - 98359830, - 1734162267, - 1243455951, - 1715081891 + 416883050, + 188953242, + 1182024076, + 59202244, + 1978179518, + 1739410190, + 679526947, + 861617775 ] }, { "data": [ - 634986620, - 1063163068, - 1040321224, - 1267702389, - 627871860, - 228913093, - 921478258, - 1324923786 + 1378484229, + 241452936, + 163273125, + 436861107, + 388667496, + 1797577532, + 443869040, + 906552846 ] }, { "data": [ - 786393727, - 991293795, - 1375401462, - 1980764312, - 680859391, - 1131959594, - 396370528, - 1759770981 + 799696786, + 1508418299, + 1856736097, + 1058842462, + 269359710, + 1099230447, + 749521578, + 520853695 ] - } - ] - } - }, - { - "path": { - "siblings": { - "data": [ - { - "data": [ - 1331122823, - 1643080471, - 525470619, - 2031212805, - 2098748101, - 1243499988, - 4104831, - 2063254342 - ] - }, - { - "data": [ - 744813374, - 813288082, - 613270960, - 679006507, - 533419743, - 993872253, - 286847881, - 1451300746 - ] - }, - { - "data": [ - 11079834, - 1639687748, - 1929217599, - 202276490, - 40122498, - 1566411931, - 675580467, - 904231754 - ] - }, - { - "data": [ - 2020782635, - 1574539412, - 1138761573, - 422111916, - 1389054530, - 1510501223, - 197381584, - 402775535 - ] - }, - { - "data": [ - 264438681, - 1087279288, - 263416095, - 1860660250, - 1429146526, - 2094709316, - 867692041, - 1662763959 - ] - }, - { - "data": [ - 544815286, - 311733778, - 799237190, - 2102805408, - 1661418854, - 1157854114, - 1855929130, - 744137327 - ] - }, - { - "data": [ - 1462886854, - 1889836598, - 2029637687, - 53625032, - 673267735, - 2047374486, - 49833992, - 1921530767 - ] - }, - { - "data": [ - 110758107, - 12788259, - 1920648715, - 413804969, - 419452419, - 1819780529, - 1147494825, - 526331496 - ] - } - ] - } - }, - "rho": { - "data": [ - 754265996, - 879244860, - 1486259768, - 2046100849, - 517389142, - 1321641711, - 698992751 - ] - }, - "hashes": { - "data": [ + }, { "data": [ - 1782166851, - 755483088, - 705928616, - 1054809239, - 1035991143, - 598933101, - 107624567, - 580522477 + 439422050, + 1286915872, + 1358195170, + 840970194, + 1786302274, + 893515818, + 370457729, + 1427474800 ] }, { "data": [ - 191975122, - 1845573414, - 1060661118, - 3844096, - 767890828, - 1256682430, - 1322161263, - 1960290303 + 2007504140, + 418866321, + 198684106, + 1996996870, + 1261455552, + 2118739919, + 56436890, + 1806594952 ] }, { "data": [ - 1186545818, - 1781761501, - 1739225478, - 720736896, - 819060565, - 601088943, - 1836159403, - 4774455 + 128324476, + 61519552, + 113352202, + 1839954511, + 2112962791, + 1831734346, + 1400107873, + 849776052 ] }, { "data": [ - 523507152, - 640464516, - 1715695303, - 1682124987, - 1442709818, - 495961733, - 1030632883, - 2056248017 + 694706906, + 2057189854, + 1419202856, + 2020454400, + 1916412209, + 304600413, + 1119212112, + 988476852 + ] + }, + { + "data": [ + 1607491401, + 916185160, + 150839959, + 1915584536, + 1192630676, + 166752571, + 44618577, + 1961530862 + ] + }, + { + "data": [ + 1949287225, + 1766709295, + 928506169, + 833212136, + 35771750, + 71835570, + 1852857681, + 1205452729 + ] + }, + { + "data": [ + 1644443152, + 1520132256, + 1370044265, + 851862297, + 261020286, + 1001533477, + 571576626, + 907308311 + ] + }, + { + "data": [ + 1557846841, + 210200770, + 685212717, + 1586976910, + 463743886, + 395493034, + 1562290362, + 1016157604 + ] + }, + { + "data": [ + 208831676, + 1180089898, + 2064964824, + 1411007716, + 1673605982, + 1643551528, + 1539845891, + 704493341 + ] + }, + { + "data": [ + 895079925, + 877096130, + 2081347331, + 124656629, + 1179296144, + 1491205760, + 356412314, + 926452265 + ] + }, + { + "data": [ + 1422286144, + 984088526, + 1135304910, + 162305405, + 1064769342, + 1110991338, + 104215457, + 1422827345 + ] + }, + { + "data": [ + 912789203, + 2010420420, + 429286304, + 295855493, + 2084240709, + 1193367228, + 1021205972, + 560375846 + ] + }, + { + "data": [ + 489627247, + 396093595, + 475714912, + 573904495, + 382358549, + 668148792, + 1416579671, + 1444313453 + ] + }, + { + "data": [ + 1345967333, + 723445075, + 2048740831, + 153155071, + 10838758, + 1236457738, + 18985351, + 1138484833 + ] + }, + { + "data": [ + 993666071, + 473860152, + 482974488, + 1244638895, + 1287107597, + 1492708811, + 1976127099, + 653628523 + ] + }, + { + "data": [ + 791701066, + 885184677, + 106955182, + 1572481724, + 1456627534, + 1452427937, + 490533016, + 991293345 + ] + }, + { + "data": [ + 578639661, + 1631171923, + 268843612, + 1788996364, + 1746080381, + 1046103170, + 455298193, + 562115509 + ] + }, + { + "data": [ + 235948816, + 1018300141, + 1002498336, + 201831066, + 1124789148, + 1994905284, + 561014981, + 1257286951 + ] + }, + { + "data": [ + 1045385620, + 1058192257, + 938515492, + 573527403, + 989948080, + 1342850602, + 1832637791, + 358929324 + ] + }, + { + "data": [ + 1902152809, + 252599905, + 623219565, + 758434560, + 640896011, + 207032991, + 835792870, + 1665795896 + ] + }, + { + "data": [ + 723715685, + 587576367, + 853971724, + 1144944495, + 873175376, + 498689849, + 43297292, + 819091873 + ] + }, + { + "data": [ + 280016656, + 437742428, + 255947140, + 349343920, + 1615039346, + 1488983802, + 1389523623, + 1912297556 + ] + }, + { + "data": [ + 901881368, + 1526942608, + 852049680, + 731288118, + 661508501, + 2010590000, + 868332352, + 1726397935 + ] + }, + { + "data": [ + 1349685696, + 2130099561, + 1462674991, + 1479393751, + 1013840091, + 1746802826, + 422993280, + 544780634 ] } ] } + }, + "rho": { + "data": [ + 1708063334, + 500631902, + 912463888, + 1859022035, + 2093407176, + 589622141, + 421358791 + ] + }, + "hashes": { + "data": [ + { + "data": [ + 1258012936, + 1759805387, + 1282523131, + 1087625667, + 2066885140, + 347413941, + 912179848, + 1182795675 + ] + }, + { + "data": [ + 2120621302, + 1146272237, + 220452956, + 1654122849, + 667076721, + 647293161, + 1473822684, + 610014272 + ] + }, + { + "data": [ + 588127966, + 1623774910, + 1981302286, + 1654815645, + 1739263748, + 476540846, + 1430988502, + 371554021 + ] + }, + { + "data": [ + 147575978, + 584959873, + 482088843, + 547413274, + 878230111, + 947438052, + 2065600800, + 1725116311 + ] + }, + { + "data": [ + 782401603, + 181111304, + 74795908, + 1495556562, + 2014424927, + 2103029287, + 626628827, + 915290649 + ] + }, + { + "data": [ + 1386657693, + 226764475, + 170886560, + 1391287227, + 241686273, + 1439085926, + 1299696477, + 224457038 + ] + }, + { + "data": [ + 156077062, + 984761927, + 636114116, + 2128285193, + 804007702, + 145764472, + 1829941080, + 897763155 + ] + }, + { + "data": [ + 832369921, + 238965180, + 160945039, + 1145687264, + 1389349131, + 1691977522, + 979195797, + 524772744 + ] + }, + { + "data": [ + 1305584230, + 1558936225, + 439307137, + 1771754638, + 875067293, + 1165888195, + 1802707435, + 1814799779 + ] + }, + { + "data": [ + 1584322090, + 1505637739, + 1431439512, + 2049043333, + 2031336532, + 1994220632, + 535798250, + 1003107923 + ] + }, + { + "data": [ + 536723909, + 1389453682, + 407278690, + 1778319839, + 1777727737, + 1948491210, + 1489215087, + 94425788 + ] + }, + { + "data": [ + 1939722636, + 1550150715, + 601277655, + 1185348003, + 205771634, + 1131394685, + 1434984545, + 971649311 + ] + }, + { + "data": [ + 1895618555, + 1937165542, + 1924755874, + 1626462217, + 1247425034, + 1370792545, + 10993310, + 259827571 + ] + }, + { + "data": [ + 960895278, + 1441935738, + 564122556, + 476582278, + 1152905344, + 187751495, + 1256558054, + 1184773204 + ] + }, + { + "data": [ + 1142655319, + 976140656, + 1227333160, + 2129498013, + 867861598, + 1790717609, + 866871241, + 1774362003 + ] + }, + { + "data": [ + 310323031, + 1437920355, + 878272104, + 722971220, + 1015159963, + 409582600, + 512066076, + 854680398 + ] + }, + { + "data": [ + 1078600753, + 1684508279, + 316224361, + 1222713314, + 336701486, + 1165314551, + 997088307, + 1438291675 + ] + }, + { + "data": [ + 1317260158, + 1861218639, + 1224575160, + 421550610, + 765270751, + 1827053147, + 289080869, + 538182924 + ] + }, + { + "data": [ + 191317502, + 911206352, + 127176973, + 1283200174, + 122086992, + 2069722074, + 1696651747, + 1805703619 + ] + }, + { + "data": [ + 1585522895, + 580813326, + 1019407832, + 475961126, + 2007366427, + 808496979, + 1181091986, + 697679912 + ] + }, + { + "data": [ + 1512587243, + 1963077188, + 1954992331, + 1545360150, + 1178760012, + 1515958126, + 705452917, + 2114456876 + ] + }, + { + "data": [ + 2049583212, + 1094272227, + 1039456282, + 787530139, + 1640279372, + 1330559514, + 240146549, + 1313599913 + ] + }, + { + "data": [ + 1716678544, + 878739389, + 47637648, + 1124863294, + 1855735812, + 648435874, + 1372962920, + 1357760622 + ] + }, + { + "data": [ + 1700444351, + 164566502, + 397969528, + 335079975, + 293991016, + 1078783808, + 326444266, + 1217021268 + ] + }, + { + "data": [ + 545406936, + 53426137, + 424114470, + 1111280438, + 618160273, + 1802384583, + 1667812411, + 783526014 + ] + }, + { + "data": [ + 305021847, + 844199180, + 1053002307, + 573437770, + 1003609966, + 752594751, + 1962990311, + 1014845114 + ] + }, + { + "data": [ + 1726913971, + 580369471, + 1458334500, + 153335379, + 781417921, + 1461588083, + 1878087297, + 1976620351 + ] + }, + { + "data": [ + 1342240957, + 1951347581, + 927989919, + 979795407, + 446565280, + 1833560107, + 1611077456, + 1708982869 + ] + }, + { + "data": [ + 1555733412, + 183459902, + 1845209457, + 294206894, + 487746799, + 1581300684, + 1098936144, + 1463828258 + ] + }, + { + "data": [ + 1627490533, + 1198683786, + 347829445, + 868233249, + 668381542, + 1667170279, + 1843656481, + 2118868916 + ] + }, + { + "data": [ + 25335971, + 1947476635, + 1202969731, + 1324303434, + 840681315, + 1530295647, + 73829885, + 2084868034 + ] + }, + { + "data": [ + 1384538139, + 1830543638, + 1993528212, + 829245670, + 987182524, + 1984193286, + 1630629317, + 671245330 + ] + }, + { + "data": [ + 1350732214, + 1458554923, + 1967947691, + 1326432866, + 2116862031, + 1830754813, + 1993865530, + 1629953044 + ] + }, + { + "data": [ + 1146542130, + 280817620, + 386152006, + 1428960819, + 1210084215, + 452674181, + 14651754, + 888508333 + ] + }, + { + "data": [ + 1560045092, + 1296963539, + 284985770, + 1434652130, + 229612754, + 1450040209, + 1958058095, + 1037043393 + ] + }, + { + "data": [ + 2020927885, + 18361940, + 653582762, + 1686120847, + 1597265575, + 28912714, + 443462147, + 870096418 + ] + }, + { + "data": [ + 1695586982, + 3373096, + 104141097, + 1042336897, + 994168241, + 1453130775, + 511038748, + 965536893 + ] + }, + { + "data": [ + 1605236949, + 127566776, + 21238712, + 434461941, + 1022175801, + 1240317127, + 1122138289, + 1008747176 + ] + }, + { + "data": [ + 2102377138, + 1530162129, + 909575023, + 1237305669, + 511960395, + 2038778105, + 287638646, + 545475552 + ] + }, + { + "data": [ + 123969828, + 595339923, + 285763600, + 1913417170, + 1555092419, + 2103200507, + 568212685, + 726567164 + ] + }, + { + "data": [ + 811207331, + 1566057452, + 346845733, + 1405783110, + 296074182, + 686180472, + 1562194090, + 1331754094 + ] + }, + { + "data": [ + 1195458345, + 1015303239, + 1769326913, + 1798476475, + 1959426322, + 263548056, + 1086173773, + 616986172 + ] + }, + { + "data": [ + 1679743849, + 267745726, + 813229082, + 1802821399, + 1106957379, + 681723311, + 38255328, + 119212296 + ] + }, + { + "data": [ + 538262759, + 561853307, + 1220138601, + 648920532, + 96368560, + 1848614699, + 564258293, + 877652518 + ] + }, + { + "data": [ + 236889586, + 1945633712, + 888366492, + 1363228903, + 1535081845, + 1843716973, + 870982648, + 2828223 + ] + }, + { + "data": [ + 1813717520, + 157322480, + 353732586, + 1058663683, + 767198951, + 185375665, + 1574055980, + 434808322 + ] + }, + { + "data": [ + 379825016, + 1815775610, + 718153065, + 878888419, + 2004655473, + 329280888, + 1716255418, + 2005381073 + ] + }, + { + "data": [ + 1590446408, + 1173249277, + 2092549673, + 208887188, + 912239485, + 796567703, + 274938304, + 390283874 + ] + }, + { + "data": [ + 779263010, + 747574741, + 1434583711, + 1620835829, + 1551673235, + 1284998639, + 679093843, + 1406669023 + ] + }, + { + "data": [ + 1291148586, + 1081265798, + 1526996412, + 391781492, + 1711281276, + 1313014433, + 1384242624, + 623027609 + ] + }, + { + "data": [ + 953015683, + 934645423, + 1771313714, + 470438654, + 1645632988, + 1239732071, + 688694286, + 1693593789 + ] + }, + { + "data": [ + 1677902343, + 45276613, + 2103891579, + 1112027086, + 161866262, + 1434591076, + 1951598120, + 772846762 + ] + }, + { + "data": [ + 551705762, + 1871931766, + 1065697665, + 283086151, + 2053411512, + 1094840383, + 1766312832, + 256750162 + ] + }, + { + "data": [ + 429680689, + 125824827, + 1965715718, + 2057352154, + 1776082615, + 2118510694, + 176499827, + 1212838505 + ] + }, + { + "data": [ + 1556722792, + 1298468122, + 657266497, + 1348176792, + 97032780, + 432903656, + 1713460397, + 236087742 + ] + }, + { + "data": [ + 268618238, + 781464872, + 1629401850, + 2084984500, + 1651362468, + 871068115, + 1722302961, + 712459750 + ] + }, + { + "data": [ + 1361188658, + 539241318, + 1966654298, + 1775313608, + 143728868, + 1546419295, + 665328088, + 2041591993 + ] + }, + { + "data": [ + 1660687505, + 535693535, + 1188238224, + 1910372027, + 441895189, + 208597526, + 1637375248, + 1439508567 + ] + }, + { + "data": [ + 1139697001, + 555025515, + 1728548969, + 1245647761, + 1604041527, + 1752808772, + 1419902779, + 1640507729 + ] + }, + { + "data": [ + 1960240298, + 666218643, + 1280441627, + 1940051826, + 1775703419, + 598652016, + 140095253, + 829013884 + ] + }, + { + "data": [ + 303840967, + 363183783, + 2079196084, + 1077588941, + 1843884294, + 229585661, + 84469314, + 1923645935 + ] + }, + { + "data": [ + 1487940169, + 725658218, + 1422188831, + 2055497525, + 1396855667, + 456348791, + 1027525060, + 1026406513 + ] + }, + { + "data": [ + 1435497940, + 276128380, + 1036933776, + 678450869, + 1197285788, + 816650348, + 240096989, + 898816825 + ] + }, + { + "data": [ + 248028907, + 940423932, + 2017860464, + 1112538086, + 866251675, + 135676603, + 1729849157, + 73108520 + ] + } + ] } - ] + } } }, "_info": { - "hash": "0x85747e3680f0686a8ee60ad657c0ea06878219df31e568b1d6ae6c58e5007fe7", + "hash": "0x879f5976fdea34463877eebb31b5f5c7c966720d6a103273499e11d7dec42c05", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet]", - "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - 3 validators in the genesis state\n - 2 additional attestations from validators 0 and 2 (in addition to proposer)\n - Verifies that all signatures are generated correctly\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n 2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This test verifies multi-validator signature scenarios:\n - Multiple XMSS keys are generated for different validators\n - Attestations from non-proposer validators are correctly verified\n - Signature aggregation works with multiple attestations (signature positions are correct)", + "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- 3 validators in the genesis state\n- 2 additional attestations from validators 0 and 2 (in addition to proposer)\n- Verifies that all signatures are generated correctly\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis test verifies multi-validator signature scenarios:\n- Multiple XMSS keys are generated for different validators\n- Attestations from non-proposer validators are correctly verified\n- Signature aggregation works with multiple attestations (signature positions are correct)", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json b/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json index 96dc2bb..14fd63e 100644 --- a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json +++ b/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json @@ -1,6 +1,7 @@ { "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet][fork_Devnet-verify_signatures_test]": { "network": "Devnet", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -30,11 +31,11 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 } ] @@ -51,8 +52,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0x2a63e3011f0b83fd7134abc2829e011dd57789ea5e44c1766d803ed877d391b7", - "stateRoot": "0xe4c2b970f38f0cd022017f58e06cff629700138c939cd5ddb19906c0de2f1c46", + "parentRoot": "0x25f6beaf778b5d1cad9d33b9bc39f7d2ed521cb3f03d8b086b6af8408617635a", + "stateRoot": "0xf8947796ac01c5ab946e51321a263b897421f03497964007001ca86e15ee4c8d", "body": { "attestations": { "data": [] @@ -64,197 +65,1206 @@ "data": { "slot": 1, "head": { - "root": "0xc185996796d67eb0a7d76c91209a0fee3f0e168ec2f571dafba2cc27377081a9", + "root": "0x16e2e465a92b9d884438754ce66e3b04e25bce25bd76ab182f8dba7502a4aca6", "slot": 1 }, "target": { - "root": "0xc185996796d67eb0a7d76c91209a0fee3f0e168ec2f571dafba2cc27377081a9", + "root": "0x16e2e465a92b9d884438754ce66e3b04e25bce25bd76ab182f8dba7502a4aca6", "slot": 1 }, "source": { - "root": "0x2a63e3011f0b83fd7134abc2829e011dd57789ea5e44c1766d803ed877d391b7", + "root": "0x25f6beaf778b5d1cad9d33b9bc39f7d2ed521cb3f03d8b086b6af8408617635a", "slot": 0 } } } }, "signature": { - "data": [ - { - "path": { - "siblings": { - "data": [ - { - "data": [ - 1331122823, - 1643080471, - 525470619, - 2031212805, - 2098748101, - 1243499988, - 4104831, - 2063254342 - ] - }, - { - "data": [ - 744813374, - 813288082, - 613270960, - 679006507, - 533419743, - 993872253, - 286847881, - 1451300746 - ] - }, - { - "data": [ - 11079834, - 1639687748, - 1929217599, - 202276490, - 40122498, - 1566411931, - 675580467, - 904231754 - ] - }, - { - "data": [ - 2020782635, - 1574539412, - 1138761573, - 422111916, - 1389054530, - 1510501223, - 197381584, - 402775535 - ] - }, - { - "data": [ - 264438681, - 1087279288, - 263416095, - 1860660250, - 1429146526, - 2094709316, - 867692041, - 1662763959 - ] - }, - { - "data": [ - 544815286, - 311733778, - 799237190, - 2102805408, - 1661418854, - 1157854114, - 1855929130, - 744137327 - ] - }, - { - "data": [ - 1462886854, - 1889836598, - 2029637687, - 53625032, - 673267735, - 2047374486, - 49833992, - 1921530767 - ] - }, - { - "data": [ - 110758107, - 12788259, - 1920648715, - 413804969, - 419452419, - 1819780529, - 1147494825, - 526331496 - ] - } - ] - } - }, - "rho": { - "data": [ - 822936441, - 1691280078, - 133155482, - 639707584, - 188230399, - 56199352, - 1344091725 - ] - }, - "hashes": { + "attestationSignatures": { + "data": [] + }, + "proposerSignature": { + "path": { + "siblings": { "data": [ { "data": [ - 1313453765, - 509566156, - 1191425365, - 1394696109, - 578431309, - 849344093, - 1900015578, - 1268473644 + 1291233891, + 901611691, + 1515447763, + 518859210, + 691456689, + 1723616255, + 1740655893, + 1397350123 + ] + }, + { + "data": [ + 1456518562, + 484811598, + 2010092021, + 282492124, + 1770180907, + 1769233778, + 494579282, + 1699827616 + ] + }, + { + "data": [ + 704838371, + 2081776305, + 559183133, + 1733361779, + 263725425, + 344472513, + 2086779080, + 1240527530 + ] + }, + { + "data": [ + 1039415328, + 308461932, + 2043838459, + 611597666, + 1580071319, + 1516124162, + 698977291, + 1585930624 + ] + }, + { + "data": [ + 250662127, + 1008190943, + 983708486, + 1247986374, + 1886580775, + 1647509743, + 1550488627, + 1260597451 + ] + }, + { + "data": [ + 416883050, + 188953242, + 1182024076, + 59202244, + 1978179518, + 1739410190, + 679526947, + 861617775 + ] + }, + { + "data": [ + 1378484229, + 241452936, + 163273125, + 436861107, + 388667496, + 1797577532, + 443869040, + 906552846 + ] + }, + { + "data": [ + 799696786, + 1508418299, + 1856736097, + 1058842462, + 269359710, + 1099230447, + 749521578, + 520853695 + ] + }, + { + "data": [ + 439422050, + 1286915872, + 1358195170, + 840970194, + 1786302274, + 893515818, + 370457729, + 1427474800 + ] + }, + { + "data": [ + 2007504140, + 418866321, + 198684106, + 1996996870, + 1261455552, + 2118739919, + 56436890, + 1806594952 + ] + }, + { + "data": [ + 128324476, + 61519552, + 113352202, + 1839954511, + 2112962791, + 1831734346, + 1400107873, + 849776052 + ] + }, + { + "data": [ + 694706906, + 2057189854, + 1419202856, + 2020454400, + 1916412209, + 304600413, + 1119212112, + 988476852 + ] + }, + { + "data": [ + 1607491401, + 916185160, + 150839959, + 1915584536, + 1192630676, + 166752571, + 44618577, + 1961530862 + ] + }, + { + "data": [ + 1949287225, + 1766709295, + 928506169, + 833212136, + 35771750, + 71835570, + 1852857681, + 1205452729 ] }, { "data": [ - 1366485659, - 1605565996, - 671158305, - 1622538715, - 394010590, - 1825233468, - 1018705001, - 1071542400 + 1644443152, + 1520132256, + 1370044265, + 851862297, + 261020286, + 1001533477, + 571576626, + 907308311 ] }, { "data": [ - 1186545818, - 1781761501, - 1739225478, - 720736896, - 819060565, - 601088943, - 1836159403, - 4774455 + 1557846841, + 210200770, + 685212717, + 1586976910, + 463743886, + 395493034, + 1562290362, + 1016157604 ] }, { "data": [ - 462810434, - 24773107, - 467766012, - 716711919, - 1288811897, - 436514417, - 852671571, - 2110518257 + 208831676, + 1180089898, + 2064964824, + 1411007716, + 1673605982, + 1643551528, + 1539845891, + 704493341 + ] + }, + { + "data": [ + 895079925, + 877096130, + 2081347331, + 124656629, + 1179296144, + 1491205760, + 356412314, + 926452265 + ] + }, + { + "data": [ + 1422286144, + 984088526, + 1135304910, + 162305405, + 1064769342, + 1110991338, + 104215457, + 1422827345 + ] + }, + { + "data": [ + 912789203, + 2010420420, + 429286304, + 295855493, + 2084240709, + 1193367228, + 1021205972, + 560375846 + ] + }, + { + "data": [ + 489627247, + 396093595, + 475714912, + 573904495, + 382358549, + 668148792, + 1416579671, + 1444313453 + ] + }, + { + "data": [ + 1345967333, + 723445075, + 2048740831, + 153155071, + 10838758, + 1236457738, + 18985351, + 1138484833 + ] + }, + { + "data": [ + 993666071, + 473860152, + 482974488, + 1244638895, + 1287107597, + 1492708811, + 1976127099, + 653628523 + ] + }, + { + "data": [ + 791701066, + 885184677, + 106955182, + 1572481724, + 1456627534, + 1452427937, + 490533016, + 991293345 + ] + }, + { + "data": [ + 578639661, + 1631171923, + 268843612, + 1788996364, + 1746080381, + 1046103170, + 455298193, + 562115509 + ] + }, + { + "data": [ + 235948816, + 1018300141, + 1002498336, + 201831066, + 1124789148, + 1994905284, + 561014981, + 1257286951 + ] + }, + { + "data": [ + 1045385620, + 1058192257, + 938515492, + 573527403, + 989948080, + 1342850602, + 1832637791, + 358929324 + ] + }, + { + "data": [ + 1902152809, + 252599905, + 623219565, + 758434560, + 640896011, + 207032991, + 835792870, + 1665795896 + ] + }, + { + "data": [ + 723715685, + 587576367, + 853971724, + 1144944495, + 873175376, + 498689849, + 43297292, + 819091873 + ] + }, + { + "data": [ + 280016656, + 437742428, + 255947140, + 349343920, + 1615039346, + 1488983802, + 1389523623, + 1912297556 + ] + }, + { + "data": [ + 901881368, + 1526942608, + 852049680, + 731288118, + 661508501, + 2010590000, + 868332352, + 1726397935 + ] + }, + { + "data": [ + 1349685696, + 2130099561, + 1462674991, + 1479393751, + 1013840091, + 1746802826, + 422993280, + 544780634 ] } ] } + }, + "rho": { + "data": [ + 716203521, + 581420617, + 1286685526, + 1194695558, + 1641401137, + 1997614743, + 496514183 + ] + }, + "hashes": { + "data": [ + { + "data": [ + 1007398320, + 1251646982, + 1612967266, + 537381478, + 93386731, + 1083280595, + 1721356609, + 1286371566 + ] + }, + { + "data": [ + 170535041, + 1162715015, + 694935546, + 306154186, + 1318628375, + 1972574425, + 1164163541, + 93198021 + ] + }, + { + "data": [ + 975961833, + 531824562, + 2035811416, + 1364843342, + 322668550, + 458809832, + 349604618, + 2092803528 + ] + }, + { + "data": [ + 654742765, + 1159278164, + 1972996313, + 523872121, + 1805821337, + 124485604, + 1193056330, + 807117735 + ] + }, + { + "data": [ + 919568569, + 2007737629, + 1957321079, + 1976407600, + 533410046, + 2111488436, + 1620339375, + 801239907 + ] + }, + { + "data": [ + 1248809976, + 857619471, + 1558705607, + 1000070635, + 536413566, + 1646494315, + 1742462035, + 361896064 + ] + }, + { + "data": [ + 1984415707, + 2120523866, + 460541868, + 1401766703, + 191855557, + 62277793, + 839423381, + 1933336793 + ] + }, + { + "data": [ + 720993176, + 565557239, + 144294658, + 1965029513, + 1320601105, + 1033475156, + 924580775, + 1891983182 + ] + }, + { + "data": [ + 2107882818, + 42805544, + 1038376944, + 1485088008, + 413502243, + 595063755, + 208296301, + 137522358 + ] + }, + { + "data": [ + 1584322090, + 1505637739, + 1431439512, + 2049043333, + 2031336532, + 1994220632, + 535798250, + 1003107923 + ] + }, + { + "data": [ + 536723909, + 1389453682, + 407278690, + 1778319839, + 1777727737, + 1948491210, + 1489215087, + 94425788 + ] + }, + { + "data": [ + 1472227604, + 777167222, + 1014016292, + 2108428626, + 1572854930, + 936945519, + 1087148360, + 239564935 + ] + }, + { + "data": [ + 1895618555, + 1937165542, + 1924755874, + 1626462217, + 1247425034, + 1370792545, + 10993310, + 259827571 + ] + }, + { + "data": [ + 1633133337, + 1030126208, + 1634656506, + 1714432182, + 923722773, + 1592896262, + 1045605719, + 1004996167 + ] + }, + { + "data": [ + 1142655319, + 976140656, + 1227333160, + 2129498013, + 867861598, + 1790717609, + 866871241, + 1774362003 + ] + }, + { + "data": [ + 310323031, + 1437920355, + 878272104, + 722971220, + 1015159963, + 409582600, + 512066076, + 854680398 + ] + }, + { + "data": [ + 562156248, + 407660686, + 123830526, + 1475625115, + 2009710949, + 611229674, + 227976023, + 1979398321 + ] + }, + { + "data": [ + 1317260158, + 1861218639, + 1224575160, + 421550610, + 765270751, + 1827053147, + 289080869, + 538182924 + ] + }, + { + "data": [ + 1373100955, + 1325801240, + 242486397, + 1594220983, + 1224055938, + 1039685355, + 1369882156, + 2105201870 + ] + }, + { + "data": [ + 882140143, + 972100438, + 1911957230, + 1132707040, + 1927731179, + 1221461913, + 7060907, + 1965761976 + ] + }, + { + "data": [ + 1512587243, + 1963077188, + 1954992331, + 1545360150, + 1178760012, + 1515958126, + 705452917, + 2114456876 + ] + }, + { + "data": [ + 2049583212, + 1094272227, + 1039456282, + 787530139, + 1640279372, + 1330559514, + 240146549, + 1313599913 + ] + }, + { + "data": [ + 1405173867, + 1816274123, + 914189354, + 1390194868, + 1875873291, + 729884524, + 1391291848, + 712390226 + ] + }, + { + "data": [ + 598163318, + 669166894, + 2095183582, + 2020485494, + 1353088122, + 288048132, + 219781410, + 2002759719 + ] + }, + { + "data": [ + 545406936, + 53426137, + 424114470, + 1111280438, + 618160273, + 1802384583, + 1667812411, + 783526014 + ] + }, + { + "data": [ + 880153551, + 1844784642, + 958326447, + 604503257, + 1004388263, + 410341879, + 1891463524, + 1293918805 + ] + }, + { + "data": [ + 501558010, + 740311244, + 1050231400, + 540493697, + 1939025889, + 865101441, + 450874438, + 556623367 + ] + }, + { + "data": [ + 14617393, + 1741137626, + 1232402672, + 1297835855, + 180473396, + 296331666, + 678243236, + 1349472775 + ] + }, + { + "data": [ + 1555733412, + 183459902, + 1845209457, + 294206894, + 487746799, + 1581300684, + 1098936144, + 1463828258 + ] + }, + { + "data": [ + 359145510, + 91313123, + 46174230, + 237526521, + 2072622391, + 1873031895, + 797080303, + 1306795272 + ] + }, + { + "data": [ + 150143807, + 1240854373, + 2107166892, + 1239041875, + 199965884, + 143519120, + 877927432, + 463537849 + ] + }, + { + "data": [ + 1279960983, + 1843209453, + 1821049971, + 1629719253, + 1678547126, + 1467417183, + 2126155977, + 29064399 + ] + }, + { + "data": [ + 2087574544, + 1055748285, + 1878614231, + 639879396, + 1324171225, + 673620585, + 1341934876, + 558856819 + ] + }, + { + "data": [ + 1584783469, + 1012855306, + 401348507, + 2091865749, + 1821226690, + 741147898, + 257249483, + 1263361762 + ] + }, + { + "data": [ + 1067822535, + 1891280943, + 813430117, + 1276106738, + 1193107083, + 1477156918, + 1539220620, + 96504038 + ] + }, + { + "data": [ + 2020927885, + 18361940, + 653582762, + 1686120847, + 1597265575, + 28912714, + 443462147, + 870096418 + ] + }, + { + "data": [ + 1393564690, + 2099610787, + 1433569094, + 1415341075, + 667347082, + 534542891, + 2086215618, + 311924734 + ] + }, + { + "data": [ + 1605236949, + 127566776, + 21238712, + 434461941, + 1022175801, + 1240317127, + 1122138289, + 1008747176 + ] + }, + { + "data": [ + 855427493, + 1207280363, + 122948393, + 1858476858, + 717680189, + 297650565, + 1852129145, + 498572861 + ] + }, + { + "data": [ + 123969828, + 595339923, + 285763600, + 1913417170, + 1555092419, + 2103200507, + 568212685, + 726567164 + ] + }, + { + "data": [ + 479007558, + 124272114, + 1475575201, + 470022555, + 470436106, + 1311385231, + 790477535, + 123444182 + ] + }, + { + "data": [ + 1851254867, + 1287818641, + 1976227070, + 2000161771, + 366470125, + 1781542280, + 130581790, + 1069901828 + ] + }, + { + "data": [ + 1246563804, + 1413964901, + 928709195, + 135099607, + 1933313698, + 942190559, + 1583299962, + 405273537 + ] + }, + { + "data": [ + 15025568, + 798031475, + 1319692199, + 626923173, + 639980038, + 1042881228, + 1087868684, + 1534522887 + ] + }, + { + "data": [ + 236889586, + 1945633712, + 888366492, + 1363228903, + 1535081845, + 1843716973, + 870982648, + 2828223 + ] + }, + { + "data": [ + 668744770, + 1762680521, + 777619164, + 842438923, + 2088233233, + 1413163967, + 2016710389, + 1505732360 + ] + }, + { + "data": [ + 428137177, + 180423701, + 422464102, + 2076710433, + 1729838960, + 535452591, + 908577683, + 35856264 + ] + }, + { + "data": [ + 1967540513, + 1519776050, + 2036007845, + 893366942, + 2089822704, + 856708567, + 52673874, + 1680933072 + ] + }, + { + "data": [ + 981208312, + 1084818190, + 677102979, + 2063847281, + 1364366814, + 1457677810, + 1899058168, + 1563619590 + ] + }, + { + "data": [ + 1291148586, + 1081265798, + 1526996412, + 391781492, + 1711281276, + 1313014433, + 1384242624, + 623027609 + ] + }, + { + "data": [ + 200567419, + 388962948, + 476521573, + 687024916, + 1833415520, + 1730904880, + 443398259, + 436157135 + ] + }, + { + "data": [ + 707230432, + 1154831848, + 281037294, + 1768624406, + 430016495, + 1598391594, + 533135163, + 1005066409 + ] + }, + { + "data": [ + 1575751610, + 754993504, + 976076502, + 28864881, + 203441435, + 1815755927, + 2032423475, + 1425030338 + ] + }, + { + "data": [ + 1562339170, + 1308262206, + 27630180, + 395740765, + 2013010851, + 1820364392, + 1927685629, + 104952625 + ] + }, + { + "data": [ + 1337781915, + 946317826, + 736367261, + 1158536580, + 1619326108, + 291263633, + 543599065, + 2111323116 + ] + }, + { + "data": [ + 252041760, + 1817414500, + 1222652907, + 729393086, + 1201147464, + 204350039, + 1423174574, + 766473914 + ] + }, + { + "data": [ + 35290624, + 543962937, + 163333824, + 329618781, + 896461446, + 346705117, + 690957194, + 1923687483 + ] + }, + { + "data": [ + 2113056063, + 1632595436, + 1471738161, + 732872543, + 1177191142, + 1142007075, + 1194993142, + 1559467243 + ] + }, + { + "data": [ + 1982272307, + 992599898, + 912060509, + 756026196, + 1317365254, + 900172012, + 1887616520, + 86671560 + ] + }, + { + "data": [ + 2046630080, + 888883591, + 84025767, + 77874323, + 1407868461, + 443546501, + 203936347, + 412038982 + ] + }, + { + "data": [ + 1135271242, + 490412616, + 1384616381, + 596851392, + 371643044, + 98437837, + 390163320, + 374739258 + ] + }, + { + "data": [ + 1465916835, + 1762678201, + 1111080241, + 460605314, + 1685336972, + 120785414, + 1657833535, + 1990363046 + ] + }, + { + "data": [ + 1622723517, + 1868134833, + 1357640922, + 2093289686, + 1317449817, + 1456398689, + 865029087, + 990587183 + ] + }, + { + "data": [ + 989042468, + 1523246160, + 349574826, + 1340646482, + 1579176982, + 725957665, + 514279194, + 572149168 + ] + } + ] } - ] + } } }, "_info": { - "hash": "0x5f7869a9585bd146ac848c4df3ef584b6c52c70fb6a3f6bc0ca6f91cbc6c0d11", + "hash": "0xfd453261ae39cec510a775702a42457e8aefbf018d4e32cad082b20bede7a8ae", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet]", - "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This is the most basic signature generation test. It verifies:\n - XMSS key generation works\n - Signature aggregation includes proposer signature", + "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis is the most basic signature generation test. It verifies:\n- XMSS key generation works\n- Signature aggregation includes proposer signature", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/validator/Cargo.toml b/lean_client/validator/Cargo.toml index 8311b9d..f38a6d6 100644 --- a/lean_client/validator/Cargo.toml +++ b/lean_client/validator/Cargo.toml @@ -6,12 +6,9 @@ edition = "2021" [features] default = ["xmss-signing"] xmss-signing = ["leansig"] -devnet1 = ["containers/devnet1", "fork-choice/devnet1", "env-config/devnet1"] -devnet2 = ["containers/devnet2", "fork-choice/devnet2", "env-config/devnet1"] [dependencies] env-config = { path = "../env-config", default-features = false } -serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" containers = { path = "../containers" } fork-choice = { path = "../fork_choice" } diff --git a/lean_client/validator/src/keys.rs b/lean_client/validator/src/keys.rs index 392fd95..7680102 100644 --- a/lean_client/validator/src/keys.rs +++ b/lean_client/validator/src/keys.rs @@ -96,7 +96,7 @@ impl KeyManager { .into()); } - // Convert to ByteVector using unsafe pointer copy (same pattern as BlsPublicKey) + // Convert to ByteVector using unsafe pointer copy (same pattern as PublicKey) let mut byte_vec: ByteVector = ByteVector::default(); unsafe { let dest = &mut byte_vec as *mut ByteVector as *mut u8; diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index 752cda8..14ab340 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -2,10 +2,8 @@ use std::collections::HashMap; use std::path::Path; -use containers::attestation::AggregatedAttestations; -#[cfg(feature = "devnet2")] -use containers::attestation::NaiveAggregatedSignature; use containers::block::BlockSignatures; +use containers::ssz; use containers::{ attestation::{Attestation, AttestationData, Signature, SignedAttestation}, block::{hash_tree_root, BlockWithAttestation, SignedBlockWithAttestation}, @@ -176,9 +174,6 @@ impl ValidatorService { .latest_new_attestations .values() .filter(|att| { - #[cfg(feature = "devnet1")] - let data = &att.message.data; - #[cfg(feature = "devnet2")] let data = &att.message; // Source must match the parent state's justified checkpoint (not store's!) let source_matches = data.source == parent_state.latest_justified; @@ -191,8 +186,7 @@ impl ValidatorService { }) .collect(); - #[cfg(feature = "devnet1")] - let valid_attestations: Vec = valid_signed_attestations + let valid_attestations: Vec = valid_signed_attestations .iter() .map(|att| att.message.clone()) .collect(); @@ -211,16 +205,6 @@ impl ValidatorService { ); // Build block with collected attestations (empty body - attestations go to state) - #[cfg(feature = "devnet1")] - let (block, _post_state, _collected_atts, sigs) = parent_state.build_block( - slot, - proposer_index, - parent_root, - Some(valid_attestations), - None, - None, - )?; - #[cfg(feature = "devnet2")] let (block, _post_state, _collected_atts, sigs) = { let valid_attestations: Vec = valid_attestations .iter() @@ -236,28 +220,12 @@ impl ValidatorService { Some(valid_attestations), None, None, + None, + None, )? }; - // Collect signatures from the attestations we included - #[cfg(feature = "devnet1")] - let mut signatures = sigs; - #[cfg(feature = "devnet2")] - let mut signatures = sigs.attestation_signatures; - for signed_att in &valid_signed_attestations { - #[cfg(feature = "devnet1")] - signatures - .push(signed_att.signature.clone()) - .map_err(|e| format!("Failed to add attestation signature: {:?}", e))?; - #[cfg(feature = "devnet2")] - { - // TODO: Use real aggregation instead of naive placeholder when spec is more up to date - let aggregated_sig: NaiveAggregatedSignature = NaiveAggregatedSignature::default(); - signatures - .push(aggregated_sig) - .map_err(|e| format!("Failed to add attestation signature: {:?}", e))?; - } - } + let signatures = sigs; info!( slot = block.slot.0, @@ -269,6 +237,8 @@ impl ValidatorService { ); // Sign the proposer attestation + let proposer_signature: Signature; + if let Some(ref key_manager) = self.key_manager { // Sign proposer attestation with XMSS let message = hash_tree_root(&proposer_attestation); @@ -276,19 +246,7 @@ impl ValidatorService { match key_manager.sign(proposer_index.0, epoch, &message.0.into()) { Ok(sig) => { - #[cfg(feature = "devnet1")] - signatures - .push(sig) - .map_err(|e| format!("Failed to add proposer signature: {:?}", e))?; - #[cfg(feature = "devnet2")] - { - // TODO: Use real aggregation instead of naive placeholder when spec is more up to date - let aggregated_sig: NaiveAggregatedSignature = - NaiveAggregatedSignature::default(); - signatures - .push(aggregated_sig) - .map_err(|e| format!("Failed to add proposer signature: {:?}", e))?; - } + proposer_signature = sig; info!(proposer = proposer_index.0, "Signed proposer attestation"); } Err(e) => { @@ -298,19 +256,28 @@ impl ValidatorService { } else { // No key manager - use zero signature warn!("Building block with zero signature (no key manager)"); + proposer_signature = Signature::default(); } + // Convert signatures to PersistentList for BlockSignatures + // Extract proof_data from AggregatedSignatureProof for wire format + let attestation_signatures = { + let mut list = ssz::PersistentList::default(); + for proof in signatures { + list.push(proof) + .map_err(|e| format!("Failed to add attestation signature: {:?}", e))?; + } + list + }; + let signed_block = SignedBlockWithAttestation { message: BlockWithAttestation { block, proposer_attestation, }, - #[cfg(feature = "devnet1")] - signature: signatures, - #[cfg(feature = "devnet2")] signature: BlockSignatures { - attestation_signatures: signatures, - proposer_signature: Signature::default(), + attestation_signatures, + proposer_signature, }, }; @@ -351,15 +318,11 @@ impl ValidatorService { .validator_indices .iter() .filter_map(|&idx| { - #[cfg(feature = "devnet1")] - let attestation = Attestation { - validator_id: Uint64(idx), - data: AttestationData { - slot, - head: head_checkpoint.clone(), - target: vote_target.clone(), - source: store.latest_justified.clone(), - }, + let attestation = AttestationData { + slot, + head: head_checkpoint.clone(), + target: vote_target.clone(), + source: store.latest_justified.clone(), }; #[cfg(feature = "devnet2")] @@ -407,24 +370,11 @@ impl ValidatorService { Signature::default() }; - { - #[cfg(feature = "devnet1")] - { - Some(SignedAttestation { - message: attestation, - signature, - }) - } - - #[cfg(feature = "devnet2")] - { - Some(SignedAttestation { - validator_id: idx, - message: attestation, - signature, - }) - } - } + Some(SignedAttestation { + validator_id: idx, + message: attestation, + signature, + }) }) .collect() } From 66ccaa1682660642ea46900d7888dd562b8872d4 Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:06:53 +0200 Subject: [PATCH 11/48] bring back test vectors for forkchoice --- lean_client/Cargo.lock | 268 +++--- lean_client/Makefile | 3 +- lean_client/fork_choice/Cargo.toml | 5 + .../tests/fork_choice_test_vectors.rs | 911 +++++++++++------- ...ation_accumulation_full_validator_set.json | 55 +- ...ttestation_superseding_same_validator.json | 35 +- ...stations_move_to_known_between_blocks.json | 35 +- ...chain_attestation_superseding_pattern.json | 95 +- ...ser_attestation_appears_in_latest_new.json | 25 +- ...lot_gaps_with_attestation_superseding.json | 55 +- ...ion_target_advances_with_attestations.json | 65 +- ...testation_target_at_genesis_initially.json | 35 +- ...station_target_justifiable_constraint.json | 315 +++--- ...ttestation_target_with_extended_chain.json | 95 +- ...est_attestation_target_with_slot_gaps.json | 45 +- ...test_head_advances_through_deep_chain.json | 217 ++--- .../test_head_switches_to_heavier_fork.json | 63 +- .../test_head_with_deep_fork_split.json | 111 +-- .../test_head_with_gaps_in_slots.json | 65 +- .../test_head_with_large_gaps.json | 55 +- .../test_head_with_two_competing_forks.json | 51 +- ...test_back_and_forth_reorg_oscillation.json | 133 +-- .../test_reorg_on_newly_justified_slot.json | 209 ++-- ..._heavy_fork_resists_light_competition.json | 139 +-- .../test_reorg_with_slot_gaps.json | 99 +- .../test_simple_one_block_reorg.json | 63 +- .../test_three_block_deep_reorg.json | 133 +-- .../test_three_way_fork_competition.json | 99 +- ..._two_block_reorg_progressive_building.json | 87 +- ...ht_forks_use_lexicographic_tiebreaker.json | 73 +- 30 files changed, 1956 insertions(+), 1683 deletions(-) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_processing/test_attestation_accumulation_full_validator_set.json (76%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_processing/test_attestation_superseding_same_validator.json (75%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_processing/test_attestations_move_to_known_between_blocks.json (77%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json (77%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json (76%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json (75%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json (71%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json (73%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json (68%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_target_selection/test_attestation_target_with_extended_chain.json (70%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json (71%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_head/test_head_advances_through_deep_chain.json (67%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_head/test_head_switches_to_heavier_fork.json (70%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_head/test_head_with_deep_fork_split.json (67%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_head/test_head_with_gaps_in_slots.json (70%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_head/test_head_with_large_gaps.json (71%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_head/test_head_with_two_competing_forks.json (71%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json (58%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json (50%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json (65%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json (67%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_simple_one_block_reorg.json (70%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_three_block_deep_reorg.json (62%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_three_way_fork_competition.json (69%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json (69%) rename lean_client/{tests/test_vectors/test_fork_choice => test_vectors/fork_choice/devnet/fc}/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json (69%) diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index 3aad917..d71fc67 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -261,7 +261,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote", + "quote 1.0.43", "syn 1.0.109", ] @@ -271,7 +271,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote", + "quote 1.0.43", "syn 1.0.109", ] @@ -281,7 +281,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ - "quote", + "quote 1.0.43", "syn 2.0.114", ] @@ -293,7 +293,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote", + "quote 1.0.43", "syn 1.0.109", ] @@ -305,8 +305,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 1.0.109", ] @@ -318,8 +318,8 @@ checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ "num-bigint", "num-traits", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -420,8 +420,8 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", "synstructure 0.13.2", ] @@ -432,8 +432,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -479,8 +479,8 @@ version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -534,8 +534,8 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -809,8 +809,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -883,9 +883,9 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.105", + "quote 1.0.43", + "unicode-xid 0.2.6", ] [[package]] @@ -1062,8 +1062,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -1085,8 +1085,8 @@ checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "strsim", "syn 2.0.114", ] @@ -1098,7 +1098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", - "quote", + "quote 1.0.43", "syn 2.0.114", ] @@ -1193,8 +1193,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 1.0.109", ] @@ -1214,11 +1214,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "rustc_version 0.4.1", "syn 2.0.114", - "unicode-xid", + "unicode-xid 0.2.6", ] [[package]] @@ -1285,8 +1285,8 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -1354,8 +1354,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" dependencies = [ "enum-ordinalize", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -1410,8 +1410,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ "heck", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -1430,8 +1430,8 @@ version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -1628,7 +1628,10 @@ version = "0.1.0" dependencies = [ "containers", "env-config", + "serde", + "serde_json", "ssz", + "test-generator", ] [[package]] @@ -1721,8 +1724,8 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -2314,8 +2317,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -2976,7 +2979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd297cf53f0cb3dee4d2620bb319ae47ef27c702684309f682bdb7e55a18ae9c" dependencies = [ "heck", - "quote", + "quote 1.0.43", "syn 2.0.114", ] @@ -3108,8 +3111,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 1.0.109", ] @@ -3247,8 +3250,8 @@ checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro-error", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 1.0.109", "synstructure 0.12.6", ] @@ -3486,8 +3489,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -3907,8 +3910,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -3991,8 +3994,8 @@ checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" dependencies = [ "pest", "pest_meta", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -4021,8 +4024,8 @@ version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -4180,8 +4183,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 1.0.109", "version_check", ] @@ -4192,11 +4195,20 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "version_check", ] +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.105" @@ -4224,8 +4236,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -4332,13 +4344,22 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.105", ] [[package]] @@ -4517,8 +4538,8 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -4623,8 +4644,8 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "regex", "relative-path", "rustc_version 0.4.1", @@ -4910,8 +4931,8 @@ version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -4971,8 +4992,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5181,8 +5202,8 @@ dependencies = [ "easy-ext", "itertools 0.14.0", "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5235,8 +5256,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5274,14 +5295,25 @@ dependencies = [ "rayon", ] +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "unicode-ident", ] @@ -5291,8 +5323,8 @@ version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "unicode-ident", ] @@ -5302,10 +5334,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 1.0.109", - "unicode-xid", + "unicode-xid 0.2.6", ] [[package]] @@ -5314,8 +5346,8 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5365,6 +5397,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "test-generator" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b23be2add79223226e1cb6446cb3e37506a5927089870687a0f1149bb7a073a" +dependencies = [ + "glob", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -5389,8 +5433,8 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5400,8 +5444,8 @@ version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5502,8 +5546,8 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5584,8 +5628,8 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -5726,6 +5770,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5905,7 +5955,7 @@ version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ - "quote", + "quote 1.0.43", "wasm-bindgen-macro-support", ] @@ -5916,8 +5966,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", "wasm-bindgen-shared", ] @@ -6035,8 +6085,8 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -6046,8 +6096,8 @@ version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -6504,8 +6554,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", "synstructure 0.13.2", ] @@ -6525,8 +6575,8 @@ version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -6545,8 +6595,8 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", "synstructure 0.13.2", ] @@ -6566,8 +6616,8 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] @@ -6599,8 +6649,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.105", + "quote 1.0.43", "syn 2.0.114", ] diff --git a/lean_client/Makefile b/lean_client/Makefile index d06597c..1a2c53a 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -54,7 +54,8 @@ generate-test-vectors: git fetch --depth 1 origin $(LEAN_SPEC_COMMIT) && \ git switch --detach FETCH_HEAD cd spec && uv run fill --clean --fork=devnet - cp -r ./spec/fixtures/consensus/* ./tests/test_vectors/ + rm -rf ./test_vectors + cp -r ./spec/fixtures/consensus/* ./test_vectors/ .PHONY: build build: diff --git a/lean_client/fork_choice/Cargo.toml b/lean_client/fork_choice/Cargo.toml index f707648..ebd5b18 100644 --- a/lean_client/fork_choice/Cargo.toml +++ b/lean_client/fork_choice/Cargo.toml @@ -10,3 +10,8 @@ default = [] env-config = { path = "../env-config", default-features = false } containers = { path = "../containers", default-features = false } ssz = { workspace = true } + +[dev-dependencies] +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +test-generator = "0.3.1" diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index 6ac6438..33ef9ee 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -1,392 +1,645 @@ -//! Fork choice test vectors for devnet2 -//! -//! Integration tests for fork choice rule implementation -//! using devnet2 data structures. +use fork_choice::{ + handlers::{on_block, on_tick}, + store::{get_forkchoice_store, Store}, +}; use containers::{ - attestation::{AttestationData, SignedAttestation}, - block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, + attestation::{Attestation, AttestationData}, + block::{ + hash_tree_root, Block, BlockBody, BlockHeader, BlockSignatures, BlockWithAttestation, + SignedBlockWithAttestation, + }, checkpoint::Checkpoint, config::Config, + public_key::PublicKey, state::State, - validator::Validator, - Bytes32, Slot, Uint64, ValidatorIndex, + AggregatedAttestation, AggregationBits, Bytes32, HistoricalBlockHashes, JustificationRoots, + JustificationsValidators, JustifiedSlots, Signature, Slot, Uint64, ValidatorIndex, Validators, }; -use fork_choice::store::{get_fork_choice_head, get_forkchoice_store, Store}; -use ssz::SszHash; -use std::collections::HashMap; - -/// Helper to create a genesis store for testing -fn create_genesis_store() -> Store { - let config = Config { genesis_time: 0 }; - let validators = vec![Validator::default(); 10]; - let state = State::generate_genesis_with_validators(Uint64(0), validators); - - let block = Block { - slot: Slot(0), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32::default(), - state_root: Bytes32(state.hash_tree_root()), - body: BlockBody::default(), - }; - let signed_block = SignedBlockWithAttestation { - message: BlockWithAttestation { - block, - proposer_attestation: Default::default(), - }, - signature: Default::default(), - }; - - get_forkchoice_store(state, signed_block, config) +use serde::Deserialize; +use ssz::{SszHash, H256}; +use std::{collections::HashMap, fs::File}; +use std::{panic::AssertUnwindSafe, path::Path}; +use test_generator::test_resources; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestCase { + #[allow(dead_code)] + network: String, + anchor_state: TestAnchorState, + anchor_block: TestAnchorBlock, + steps: Vec, + #[serde(rename = "_info")] + info: TestInfo, } -/// Helper to create a signed attestation -fn create_attestation( - validator_id: u64, +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestAnchorState { + config: TestConfig, slot: u64, - head: Checkpoint, - target: Checkpoint, - source: Checkpoint, -) -> SignedAttestation { - SignedAttestation { - validator_id, - message: AttestationData { - slot: Slot(slot), - head, - target, - source, - }, - signature: Default::default(), - } + latest_block_header: TestBlockHeader, + latest_justified: TestCheckpoint, + latest_finalized: TestCheckpoint, + #[serde(default)] + historical_block_hashes: TestDataWrapper, + #[serde(default)] + justified_slots: TestDataWrapper, + validators: TestDataWrapper, + #[serde(default)] + justifications_roots: TestDataWrapper, + #[serde(default)] + justifications_validators: TestDataWrapper, } -/// Helper to add a block to the store -fn add_block(store: &mut Store, slot: u64, parent_root: Bytes32, proposer: u64) -> Bytes32 { - let block = Block { - slot: Slot(slot), - proposer_index: ValidatorIndex(proposer), - parent_root, - state_root: Bytes32::default(), - body: BlockBody::default(), - }; - let block_root = Bytes32(block.hash_tree_root()); +impl Into for TestAnchorState { + fn into(self) -> State { + let config = self.config.into(); + + let latest_block_header = self.latest_block_header.into(); + + let mut historical_block_hashes = HistoricalBlockHashes::default(); + for hash_str in &self.historical_block_hashes.data { + historical_block_hashes + .push(parse_root(hash_str)) + .expect("within limit"); + } + + let mut justified_slots = JustifiedSlots::new(false, self.justified_slots.data.len()); + for (i, &val) in self.justified_slots.data.iter().enumerate() { + if val { + justified_slots.set(i, true); + } + } + + let mut justifications_roots = JustificationRoots::default(); + for root_str in &self.justifications_roots.data { + justifications_roots + .push(parse_root(root_str)) + .expect("within limit"); + } + + let mut justifications_validators = + JustificationsValidators::new(false, self.justifications_validators.data.len()); + for (i, &val) in self.justifications_validators.data.iter().enumerate() { + if val { + justifications_validators.set(i, true); + } + } + + let mut validators = Validators::default(); + for test_validator in &self.validators.data { + let pubkey = PublicKey::from_hex(&test_validator.pubkey) + .expect("Failed to parse validator pubkey"); + let validator = containers::validator::Validator { + pubkey, + index: containers::Uint64(test_validator.index), + }; + validators.push(validator).expect("Failed to add validator"); + } + + State { + config, + slot: Slot(self.slot), + latest_block_header, + latest_justified: self.latest_justified.into(), + latest_finalized: self.latest_finalized.into(), + historical_block_hashes, + justified_slots, + validators, + justifications_roots, + justifications_validators, + } + } +} - store.blocks.insert( - block_root, - SignedBlockWithAttestation { - message: BlockWithAttestation { - block, - proposer_attestation: Default::default(), - }, - signature: Default::default(), - }, - ); +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestConfig { + genesis_time: u64, +} - block_root +impl Into for TestConfig { + fn into(self) -> Config { + Config { + genesis_time: self.genesis_time, + } + } } -#[test] -fn test_genesis_state_transition() { - let store = create_genesis_store(); +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestBlockHeader { + slot: u64, + proposer_index: u64, + parent_root: String, + state_root: String, + body_root: String, +} - // Verify genesis state is properly initialized - assert!(!store.head.0.is_zero()); - assert_eq!(store.blocks.len(), 1); - assert_eq!(store.states.len(), 1); +impl Into for TestBlockHeader { + fn into(self) -> BlockHeader { + BlockHeader { + slot: Slot(self.slot), + proposer_index: ValidatorIndex(self.proposer_index), + parent_root: parse_root(&self.parent_root), + state_root: parse_root(&self.state_root), + body_root: parse_root(&self.body_root), + } + } +} - // Genesis should be both justified and finalized - assert_eq!(store.latest_justified.slot, Slot(0)); - assert_eq!(store.latest_finalized.slot, Slot(0)); +#[derive(Debug, Deserialize)] +struct TestCheckpoint { + root: String, + slot: u64, } -#[test] -fn test_basic_slot_transition() { - let mut store = create_genesis_store(); - let genesis_root = store.head; +impl Into for TestCheckpoint { + fn into(self) -> Checkpoint { + Checkpoint { + root: parse_root(&self.root), + slot: Slot(self.slot), + } + } +} - // Add blocks at slots 1, 2, 3 - let block1_root = add_block(&mut store, 1, genesis_root, 0); - let block2_root = add_block(&mut store, 2, block1_root, 0); - let block3_root = add_block(&mut store, 3, block2_root, 0); +#[derive(Debug, Deserialize, Default)] +struct TestDataWrapper { + data: Vec, +} - assert_eq!(store.blocks.len(), 4); +#[derive(Debug, Deserialize)] +struct TestValidator { + #[allow(dead_code)] + pubkey: String, + #[allow(dead_code)] + #[serde(default)] + index: u64, +} - // Without attestations and min_votes=1, head should stay at genesis - // (no blocks have enough votes to be considered) - let empty_attestations = HashMap::new(); - let head = get_fork_choice_head(&store, genesis_root, &empty_attestations, 1); - assert_eq!(head, genesis_root); +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestAnchorBlock { + slot: u64, + proposer_index: u64, + parent_root: String, + state_root: String, + body: TestBlockBody, +} - // With attestation for block3 and min_votes=1, head should follow the voted chain - let mut attestations = HashMap::new(); - let checkpoint = Checkpoint { - root: block3_root, - slot: Slot(3), - }; - let genesis_checkpoint = Checkpoint { - root: genesis_root, - slot: Slot(0), - }; +impl Into for TestAnchorBlock { + fn into(self) -> SignedBlockWithAttestation { + let mut attestations = ssz::PersistentList::default(); + + for (i, attestation) in self.body.attestations.data.into_iter().enumerate() { + attestations + .push(attestation.into()) + .expect(&format!("Failed to add attestation {}", i)); + } + + let block = Block { + slot: Slot(self.slot), + proposer_index: ValidatorIndex(self.proposer_index), + parent_root: parse_root(&self.parent_root), + state_root: parse_root(&self.state_root), + body: BlockBody { attestations }, + }; + + // Create proposer attestation + let proposer_attestation = Attestation { + validator_id: Uint64(self.proposer_index), + data: AttestationData { + slot: Slot(self.slot), + head: Checkpoint { + root: parse_root(&self.parent_root), + slot: Slot(self.slot), + }, + target: Checkpoint { + root: parse_root(&self.parent_root), + slot: Slot(self.slot), + }, + source: Checkpoint { + root: parse_root(&self.parent_root), + slot: Slot(0), + }, + }, + }; - attestations.insert( - ValidatorIndex(0), - create_attestation( - 0, - 3, - checkpoint.clone(), - checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); - - // The fork choice should follow the chain with votes to find the heaviest head - let head = get_fork_choice_head(&store, genesis_root, &attestations, 1); - - // With 1 vote on block3, the entire chain block1->block2->block3 gets 1 vote each - // So head should be block3 (the tip of the voted chain) - assert_eq!(head, block3_root); -} - -#[test] -fn test_attestation_processing() { - let mut store = create_genesis_store(); - let genesis_root = store.head; - let genesis_checkpoint = Checkpoint { - root: genesis_root, - slot: Slot(0), - }; + SignedBlockWithAttestation { + message: BlockWithAttestation { + block, + proposer_attestation, + }, + signature: BlockSignatures::default(), + } + } +} - // Create a block - let block1_root = add_block(&mut store, 1, genesis_root, 0); - let block1_checkpoint = Checkpoint { - root: block1_root, - slot: Slot(1), - }; +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestBlock { + slot: u64, + proposer_index: u64, + parent_root: String, + state_root: String, + body: TestBlockBody, +} - // Process attestations from multiple validators - let mut attestations = HashMap::new(); - for i in 0..5 { - attestations.insert( - ValidatorIndex(i), - create_attestation( - i, - 1, - block1_checkpoint.clone(), - block1_checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); +impl Into for TestBlock { + fn into(self) -> Block { + Block { + slot: Slot(self.slot), + proposer_index: ValidatorIndex(self.proposer_index), + parent_root: parse_root(&self.parent_root), + state_root: parse_root(&self.parent_root), + body: self.body.into(), + } } - - let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); - assert_eq!(head, block1_root); } -#[test] -fn test_multiple_attestations() { - let mut store = create_genesis_store(); - let genesis_root = store.head; - let genesis_checkpoint = Checkpoint { - root: genesis_root, - slot: Slot(0), - }; +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestBlockWithAttestation { + block: TestBlock, + proposer_attestation: TestAttestation, + #[serde(default)] + block_root_label: Option, +} - // Create a chain of blocks - let block1_root = add_block(&mut store, 1, genesis_root, 0); - let block2_root = add_block(&mut store, 2, block1_root, 0); - let block3_root = add_block(&mut store, 3, block2_root, 0); +impl Into for TestBlockWithAttestation { + fn into(self) -> BlockWithAttestation { + BlockWithAttestation { + block: self.block.into(), + proposer_attestation: self.proposer_attestation.into(), + } + } +} - let block3_checkpoint = Checkpoint { - root: block3_root, - slot: Slot(3), - }; +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestAttestation { + validator_id: u64, + data: TestAttestationData, +} - // All validators attest to block3 - let mut attestations = HashMap::new(); - for i in 0..10 { - attestations.insert( - ValidatorIndex(i), - create_attestation( - i, - 3, - block3_checkpoint.clone(), - block3_checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); +impl Into for TestAttestation { + fn into(self) -> Attestation { + Attestation { + validator_id: Uint64(self.validator_id), + data: self.data.into(), + } } - - let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); - assert_eq!(head, block3_root); } -#[test] -fn test_fork_choice_with_competing_blocks() { - let mut store = create_genesis_store(); - let genesis_root = store.head; - let genesis_checkpoint = Checkpoint { - root: genesis_root, - slot: Slot(0), - }; +#[derive(Debug, Deserialize)] +struct TestBlockBody { + attestations: TestDataWrapper, +} - // Create two competing forks at slot 1 - let fork_a_root = add_block(&mut store, 1, genesis_root, 0); - let fork_b_root = add_block(&mut store, 1, genesis_root, 1); // Different proposer +impl Into for TestBlockBody { + fn into(self) -> BlockBody { + let mut attestations = ssz::PersistentList::default(); - let fork_a_checkpoint = Checkpoint { - root: fork_a_root, - slot: Slot(1), - }; - let fork_b_checkpoint = Checkpoint { - root: fork_b_root, - slot: Slot(1), - }; + for attestation in self.attestations.data { + attestations + .push(attestation.into()) + .expect("failed to add attestation"); + } - // 6 validators vote for fork A - let mut attestations = HashMap::new(); - for i in 0..6 { - attestations.insert( - ValidatorIndex(i), - create_attestation( - i, - 1, - fork_a_checkpoint.clone(), - fork_a_checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); + BlockBody { attestations } } +} - // 4 validators vote for fork B - for i in 6..10 { - attestations.insert( - ValidatorIndex(i), - create_attestation( - i, - 1, - fork_b_checkpoint.clone(), - fork_b_checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestAggregatedAttestation { + aggregation_bits: TestAggregationBits, + data: TestAttestationData, +} + +impl Into for TestAggregatedAttestation { + fn into(self) -> AggregatedAttestation { + AggregatedAttestation { + aggregation_bits: self.aggregation_bits.data, + data: self.data.into(), + } } +} - let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); +#[derive(Debug, Deserialize)] +struct TestAggregationBits { + data: AggregationBits, +} - // Fork A should win with more votes - assert_eq!(head, fork_a_root); +#[derive(Debug, Deserialize)] +struct TestAttestationData { + slot: u64, + head: TestCheckpoint, + target: TestCheckpoint, + source: TestCheckpoint, } -#[test] -fn test_finality_prevents_reorg() { - let mut store = create_genesis_store(); - let genesis_root = store.head; - let genesis_checkpoint = Checkpoint { - root: genesis_root, - slot: Slot(0), - }; +impl Into for TestAttestationData { + fn into(self) -> AttestationData { + AttestationData { + slot: Slot(self.slot), + head: self.head.into(), + target: self.target.into(), + source: self.source.into(), + } + } +} - // Create a finalized chain - let block1_root = add_block(&mut store, 1, genesis_root, 0); - let block2_root = add_block(&mut store, 2, block1_root, 0); +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestStep { + valid: bool, + #[serde(default)] + checks: Option, + #[serde(rename = "stepType")] + step_type: String, + block: Option, + attestation: Option, + tick: Option, + time: Option, +} - // Update finalized checkpoint - store.latest_finalized = Checkpoint { - root: block1_root, - slot: Slot(1), - }; +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestChecks { + #[serde(rename = "headSlot")] + head_slot: Option, + #[serde(rename = "headRootLabel")] + head_root_label: Option, + #[serde(rename = "attestationChecks")] + attestation_checks: Option>, +} - // Create competing fork from genesis (should not be chosen due to finality) - let competing_root = add_block(&mut store, 1, genesis_root, 1); +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct AttestationCheck { + validator: u64, + #[allow(dead_code)] + #[serde(rename = "attestationSlot")] + attestation_slot: u64, + #[serde(rename = "targetSlot")] + target_slot: Option, + location: String, +} - let block2_checkpoint = Checkpoint { - root: block2_root, - slot: Slot(2), - }; - let competing_checkpoint = Checkpoint { - root: competing_root, - slot: Slot(1), - }; +#[derive(Debug, Deserialize)] +struct TestInfo { + #[allow(dead_code)] + hash: String, + #[allow(dead_code)] + comment: String, + #[serde(rename = "testId")] + test_id: String, + #[allow(dead_code)] + description: String, + #[allow(dead_code)] + #[serde(rename = "fixtureFormat")] + fixture_format: String, +} - // More votes for competing fork - let mut attestations = HashMap::new(); - for i in 0..7 { - attestations.insert( - ValidatorIndex(i), - create_attestation( - i, - 1, - competing_checkpoint.clone(), - competing_checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); +fn parse_root(hex_str: &str) -> Bytes32 { + let hex = hex_str.trim_start_matches("0x"); + let mut bytes = [0u8; 32]; + + if hex.len() == 64 { + for i in 0..32 { + bytes[i] = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16) + .unwrap_or_else(|_| panic!("Invalid hex at position {}: {}", i, hex)); + } + } else if !hex.chars().all(|c| c == '0') { + panic!("Invalid root length: {} (expected 64 hex chars)", hex.len()); } - for i in 7..10 { - attestations.insert( - ValidatorIndex(i), - create_attestation( - i, - 2, - block2_checkpoint.clone(), - block2_checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); - } - - // Start from finalized block1 - let head = get_fork_choice_head(&store, block1_root, &attestations, 0); - // Should follow the chain from block1, not competing fork - assert_eq!(head, block2_root); + Bytes32(ssz::H256::from(bytes)) } -#[test] -fn test_attestation_from_future_slot() { - let mut store = create_genesis_store(); - let genesis_root = store.head; - let genesis_checkpoint = Checkpoint { - root: genesis_root, - slot: Slot(0), +fn verify_checks( + store: &Store, + checks: &Option, + block_labels: &HashMap, + step_idx: usize, +) -> Result<(), String> { + // If no checks provided, nothing to verify + let checks = match checks { + Some(c) => c, + None => return Ok(()), }; - // Create block at slot 1 - let block1_root = add_block(&mut store, 1, genesis_root, 0); - let block1_checkpoint = Checkpoint { - root: block1_root, - slot: Slot(1), - }; + if let Some(expected_slot) = checks.head_slot { + let actual_slot = store.blocks[&store.head].message.block.slot.0; + if actual_slot != expected_slot { + return Err(format!( + "Step {}: Head slot mismatch - expected {}, got {}", + step_idx, expected_slot, actual_slot + )); + } + } - // Attestation claims to be from slot 100 (future) - // The fork choice still processes it based on what block it points to - let mut attestations = HashMap::new(); - attestations.insert( - ValidatorIndex(0), - create_attestation( - 0, - 100, - block1_checkpoint.clone(), - block1_checkpoint.clone(), - genesis_checkpoint.clone(), - ), - ); + if let Some(label) = &checks.head_root_label { + let expected_root = block_labels + .get(label) + .ok_or_else(|| format!("Step {}: Block label '{}' not found", step_idx, label))?; + if &store.head != expected_root { + let actual_slot = store + .blocks + .get(&store.head) + .map(|b| b.message.block.slot.0) + .unwrap_or(0); + let expected_slot = store + .blocks + .get(expected_root) + .map(|b| b.message.block.slot.0) + .unwrap_or(0); + return Err(format!( + "Step {}: Head root mismatch for label '{}' - expected slot {}, got slot {} (known_attestations: {}, new_attestations: {})", + step_idx, label, expected_slot, actual_slot, + store.latest_known_attestations.len(), store.latest_new_attestations.len() + )); + } + } - let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); + if let Some(att_checks) = &checks.attestation_checks { + for check in att_checks { + let validator = ValidatorIndex(check.validator); + + match check.location.as_str() { + "new" => { + if !store.latest_new_attestations.contains_key(&validator) { + return Err(format!( + "Step {}: Expected validator {} in new attestations, but not found", + step_idx, check.validator + )); + } + if let Some(target_slot) = check.target_slot { + let attestation = &store.latest_new_attestations[&validator]; + if attestation.message.target.slot.0 != target_slot { + return Err(format!( + "Step {}: Validator {} new attestation target slot mismatch - expected {}, got {}", + step_idx, check.validator, target_slot, attestation.message.target.slot.0 + )); + } + } + } + "known" => { + if !store.latest_known_attestations.contains_key(&validator) { + return Err(format!( + "Step {}: Expected validator {} in known attestations, but not found", + step_idx, check.validator + )); + } + } + _ => { + return Err(format!( + "Step {}: Unknown attestation location: {}", + step_idx, check.location + )); + } + } + } + } - // Should still follow the attestation to block1 - assert_eq!(head, block1_root); + Ok(()) } -#[test] -fn test_empty_attestations_returns_root() { - let store = create_genesis_store(); - let genesis_root = store.head; - - let empty_attestations = HashMap::new(); - let head = get_fork_choice_head(&store, genesis_root, &empty_attestations, 0); - - // With no attestations, should return the provided root - assert_eq!(head, genesis_root); +#[test_resources("test_vectors/fork_choice/*/fc/*/*.json")] +fn forkchoice(spec_file: &str) { + let spec_path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join(spec_file); + let mut file = + File::open(&spec_path).expect(&format!("failed to open spec file {spec_path:?}")); + let test_cases: HashMap = serde_json::from_reader(&mut file).unwrap(); + + for (_, case) in test_cases { + let config = Config { + genesis_time: case.anchor_state.config.genesis_time, + }; + + let mut anchor_state: State = case.anchor_state.into(); + let anchor_block: SignedBlockWithAttestation = case.anchor_block.into(); + + let body_root = hash_tree_root(&anchor_block.message.block.body); + anchor_state.latest_block_header = BlockHeader { + slot: anchor_block.message.block.slot, + proposer_index: anchor_block.message.block.proposer_index, + parent_root: anchor_block.message.block.parent_root, + state_root: anchor_block.message.block.state_root, + body_root, + }; + + let mut store = get_forkchoice_store(anchor_state, anchor_block, config); + let mut block_labels: HashMap = HashMap::new(); + + for (step_idx, step) in case.steps.into_iter().enumerate() { + match step.step_type.as_str() { + "block" => { + let test_block = step + .block + .expect(&format!("Step {step_idx}: Missing block data")); + + let block_root_label = test_block.block_root_label.clone(); + + let result = std::panic::catch_unwind(AssertUnwindSafe(|| { + let block: BlockWithAttestation = test_block.into(); + let signed_block: SignedBlockWithAttestation = SignedBlockWithAttestation { + message: block, + signature: BlockSignatures::default(), + }; + let block_root = Bytes32(signed_block.message.block.hash_tree_root()); + + // Advance time to the block's slot to ensure attestations are processable + // SECONDS_PER_SLOT is 4 (not 12) + let block_time = + store.config.genesis_time + (signed_block.message.block.slot.0 * 4); + on_tick(&mut store, block_time, false); + + on_block(&mut store, signed_block)?; + Ok(block_root) + })); + + let result = match result { + Ok(inner) => inner, + Err(e) => Err(format!("Panic: {:?}", e)), + }; + + if let Ok(block_root) = &result { + if let Some(label) = block_root_label { + block_labels.insert(label.clone(), *block_root); + } + } + + if step.valid && result.is_err() { + panic!( + "Step {step_idx}: Block should be valid but processing failed: {:?}", + result.err().unwrap() + ); + } else if !step.valid && result.is_ok() { + panic!( + "Step: {step_idx}: Block should be invalid but processing succeeded" + ); + } + + if step.valid && result.is_ok() { + verify_checks(&store, &step.checks, &block_labels, step_idx).expect( + &format!("Step: {step_idx}: Should be valid but checks failed"), + ); + } + } + "tick" | "time" => { + let time_value = step + .tick + .or(step.time) + .expect(&format!("Step {step_idx}: Missing tick/time data")); + on_tick(&mut store, time_value, false); + + if step.valid { + verify_checks(&store, &step.checks, &block_labels, step_idx).expect( + &format!("Step: {step_idx}: Should be valid but checks failed"), + ); + } + } + // "attestation" => { + // let test_att = step + // .attestation + // .as_ref() + // .expect(&format!("Step {}: Missing attestation data", step_idx)); + + // let result = std::panic::catch_unwind(AssertUnwindSafe(|| { + // let attestation: AttestationData = test_att.into(); + // let signed_attestation = SignedAttestation { + // message: attestation, + // signature: Signature::default(), + // }; + // on_attestation(&mut store, signed_attestation, false) + // })); + + // let result = match result { + // Ok(inner) => inner, + // Err(e) => Err(format!("Panic: {:?}", e)), + // }; + + // if step.valid && result.is_err() { + // panic!("Step {step_idx}: Attestation should be valid but processing failed: {:?}", result.err().unwrap()); + // } else if !step.valid && result.is_ok() { + // panic!("Step {step_idx}: Attestation should be invalid but processing succeeded"); + // } + + // if step.valid && result.is_ok() { + // verify_checks(&store, &step.checks, &block_labels, step_idx)?; + // } + // } + _ => { + panic!("Step {step_idx}: Unknown step type: {}", step.step_type); + } + } + } + } } diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestation_accumulation_full_validator_set.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json similarity index 76% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestation_accumulation_full_validator_set.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json index 9b955fe..2f9d0f0 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestation_accumulation_full_validator_set.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_accumulation_full_validator_set[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_accumulation_full_validator_set[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -84,8 +85,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -97,15 +98,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -135,8 +136,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -148,15 +149,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -191,8 +192,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -204,15 +205,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -252,8 +253,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -265,15 +266,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -283,7 +284,7 @@ ], "maxSlot": 4, "_info": { - "hash": "0x90184ec647e79ca6d5e638cedcb69c8f80ca08cf1096db66e430e9fa8eca3521", + "hash": "0x20a5a607df3a6b554e24236dd46d2e751befee0a2b04716e5c2022251881d54a", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_accumulation_full_validator_set[fork_Devnet]", "description": "All validators contribute attestations across both dictionaries.\n\n Scenario\n --------\n Process blocks at slots 1, 2, 3, 4 (complete validator rotation).\n\n Expected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - After slot 3: new attestations = 1, known attestations = 2\n - After slot 4: new attestations = 1, known attestations = 3 (total: 4 validators)\n\n Why This Matters\n ----------------\n With 4 validators and consecutive blocks, each validator proposes once.\n\n Attestations accumulate across both dictionaries:\n - new: current slot's proposer\n - known: all previous proposers\n\n The total (new + known) equals the number of unique validators who proposed.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestation_superseding_same_validator.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json similarity index 75% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestation_superseding_same_validator.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json index b845054..20b33c4 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestation_superseding_same_validator.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_superseding_same_validator[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_superseding_same_validator[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -86,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -99,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -133,8 +134,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0x2c22cdcafc652fcaee096667343c4c6ddf40ae2420a33f8e6026726193a40bad", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xd13d7468177a38a08f5e93fdbca977304166c9a4abaa6ed54b67e456fc27a965", "body": { "attestations": { "data": [] @@ -146,15 +147,15 @@ "data": { "slot": 5, "head": { - "root": "0x196d1ce0ec21aae75ece42e7cf8993d0bb05994e9140b5bf3b7c7942e6f23d19", + "root": "0x2d863bd6e2498d9d8e103c2c7b450e6e27cfcc39fb2e18bfda30076b2a582ebf", "slot": 5 }, "target": { - "root": "0x196d1ce0ec21aae75ece42e7cf8993d0bb05994e9140b5bf3b7c7942e6f23d19", + "root": "0x2d863bd6e2498d9d8e103c2c7b450e6e27cfcc39fb2e18bfda30076b2a582ebf", "slot": 5 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -164,7 +165,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0x8a7a70694df9bcf2c925f2a5c5a36df6215f0b942f9c984f6b82d62f2f7adffe", + "hash": "0xf2546ddba6f4e0623514d90f314e58b92cccacc40b45fc88c6b894060534d3e4", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_superseding_same_validator[fork_Devnet]", "description": "Newer attestation from same validator supersedes older attestation.\n\n Scenario\n --------\n Process blocks at slots 1 and 5 (same proposer: validator 1).\n\n Expected:\n - After slot 1: validator 1 attests to slot 1\n - After slot 5: validator 1 attests to slot 5 (supersedes slot 1)\n\n Why This Matters\n ----------------\n With round-robin proposer selection, slots 1 and 5 use the same validator.\n\n When that validator proposes again, their newer attestation supersedes the older one.\n Both dictionaries are keyed by validator index, so only the most recent\n attestation per validator is retained.\n\n Key insight: Attestations accumulate across validators but supersede within validators.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestations_move_to_known_between_blocks.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json similarity index 77% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestations_move_to_known_between_blocks.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json index 0aa2036..942d14b 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_attestations_move_to_known_between_blocks.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestations_move_to_known_between_blocks[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestations_move_to_known_between_blocks[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -86,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -99,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -142,8 +143,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -155,15 +156,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -173,7 +174,7 @@ ], "maxSlot": 2, "_info": { - "hash": "0xe88c8f4ed7763ea81178bd5634a71b7c8bc3617291986c0f9c6fce9719e441fe", + "hash": "0x23dbfac62912e991a25db677805a292bb1aa6328a62c3796b733eb6c9a1d903a", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestations_move_to_known_between_blocks[fork_Devnet]", "description": "Attestations move from latest_new to latest_known between blocks.\n\n Scenario\n --------\n Process blocks at slots 1 and 2 (different proposers: validators 1 and 2).\n\n Expected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - Validator 1's attestation moved to known with correct checkpoints\n - Validator 2's attestation in new with correct checkpoints\n\n Why This Matters\n ----------------\n The interval tick system drives attestation migration between slots.\n\n Before processing the next block, interval ticks move all attestations from\n new \u2192 known and clear the new dictionary. Then the next block's proposer\n attestation enters the now-empty new dictionary.\n\n This creates the attestation pipeline:\n - Enter via new (arrivals)\n - Graduate to known (accepted for fork choice)", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json similarity index 77% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json index 17b8356..33780b1 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_processing.py::test_extended_chain_attestation_superseding_pattern[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_processing.py::test_extended_chain_attestation_superseding_pattern[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -83,8 +84,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -96,15 +97,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -133,8 +134,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -146,15 +147,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -188,8 +189,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -201,15 +202,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -248,8 +249,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -261,15 +262,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -308,8 +309,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -321,15 +322,15 @@ "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 } } @@ -368,8 +369,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", - "stateRoot": "0x3b523ede40789338d432b631a65579b3855c2861c90c44cf760e9e9bc6413cdc", + "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", "body": { "attestations": { "data": [] @@ -381,15 +382,15 @@ "data": { "slot": 6, "head": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "target": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "source": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 } } @@ -428,8 +429,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", - "stateRoot": "0xf1f50a275eee0692aa7c36132d2ee64ddecfa4f95c59627d723a4404a7ff4382", + "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", "body": { "attestations": { "data": [] @@ -441,15 +442,15 @@ "data": { "slot": 7, "head": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "target": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "source": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 } } @@ -488,8 +489,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", - "stateRoot": "0xa90ee765af23b009dff6abf61532da66fc61f2d42940ecca2d87cbfa5916e886", + "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", "body": { "attestations": { "data": [] @@ -501,15 +502,15 @@ "data": { "slot": 8, "head": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "target": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "source": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 } } @@ -519,7 +520,7 @@ ], "maxSlot": 8, "_info": { - "hash": "0xaf9d1a0115b2c77193c5f35881dfbe8e239ee89fbb61553f9b95cf007049c72e", + "hash": "0x8aa34f8496e6fa348aa18faa45fe3e3d3b4b476fb2b65309312839ab889abb56", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_extended_chain_attestation_superseding_pattern[fork_Devnet]", "description": "Attestation superseding pattern over two complete validator rotations.\n\n Scenario\n --------\n Process blocks at slots 1-8 (two complete validator rotations).\n\n Phase 1 (slots 1-4): Accumulation\n Validators each propose once, attestations accumulate to 4 total.\n\n Phase 2 (slots 5-8): Steady State\n Validators propose again, newer attestations supersede older ones.\n Total stays at 4, composition changes.\n\n Expected:\n - After slot 4: All 4 validators have attestations (v0 in new, v1-v3 in known)\n - After slot 5: Validator 1 supersedes their slot 1 attestation\n - After slot 8: All validators have their latest attestations from slots 5-8\n\n Why This Matters\n ----------------\n The system reaches steady state: one attestation per validator.\n\n As each validator proposes again, their new attestation supersedes their old one.\n The count remains constant (4), but the composition updates.\n\n This confirms superseding maintains correct state over time with no attestation\n leaks or unbounded growth.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json similarity index 76% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json index b145f7a..b03c7e7 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_processing.py::test_proposer_attestation_appears_in_latest_new[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_processing.py::test_proposer_attestation_appears_in_latest_new[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -86,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -99,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -117,7 +118,7 @@ ], "maxSlot": 1, "_info": { - "hash": "0x26adec7122d892aae7bcc8333cf0fb60d92e2960bd2187bc2aa4f6374ed6ad41", + "hash": "0xa1b9de2d8ff812fc338af7ce83c6f13c839a7e4cae92a23f9ce9f459a9508586", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_proposer_attestation_appears_in_latest_new[fork_Devnet]", "description": "Proposer attestation appears in latest_new after block processing.\n\n Scenario\n --------\n Process one block at slot 1 (proposer: validator 1).\n\n Expected:\n - validator 1's attestation has correct slot and checkpoint slots\n\n Why This Matters\n ----------------\n New proposer attestations enter the pipeline through `latest_new_attestations`,\n not directly into `latest_known_attestations`.\n\n This baseline test verifies the entry point of the attestation pipeline.\n All new attestations must enter through the \"new\" stage before graduating to \"known\".", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json similarity index 75% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json index f7e01c4..6e9111c 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_processing.py::test_slot_gaps_with_attestation_superseding[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_processing.py::test_slot_gaps_with_attestation_superseding[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -84,8 +85,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -97,15 +98,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -135,8 +136,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xcd17edc02a50383378ff96848805bbcea1b61b90a770da57d1114c584e8c6f19", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0x7031a4d5385dcfbe2a9f373845bb8ffd86863aed0d0d87976141c9b11edac5bc", "body": { "attestations": { "data": [] @@ -148,15 +149,15 @@ "data": { "slot": 3, "head": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 }, "target": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -186,8 +187,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", - "stateRoot": "0x9e0f5e90d9aa8238657d801bd6ca56fba6d503154fda163947d1de956593b38d", + "parentRoot": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "stateRoot": "0x1ee1961b8157e69e77990196fbab902d16c0f51bd8da1848dfa0a6d8e177ad7c", "body": { "attestations": { "data": [] @@ -199,15 +200,15 @@ "data": { "slot": 5, "head": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 }, "target": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 }, "source": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 } } @@ -237,8 +238,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", - "stateRoot": "0xcc162a538e469351494e5d7e5e73ad35e73e847b8ad8d3ab98354ebc0897cde9", + "parentRoot": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "stateRoot": "0x784f0f32d518e8a5ccdc7b0887d26d64b2d0048faa696c0616d8ff5a37d02d28", "body": { "attestations": { "data": [] @@ -250,15 +251,15 @@ "data": { "slot": 7, "head": { - "root": "0x550c43967f27c9b0dfe97cdebc8dcfc392b34e18108f2ab012dce9234e7a43ee", + "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", "slot": 7 }, "target": { - "root": "0x550c43967f27c9b0dfe97cdebc8dcfc392b34e18108f2ab012dce9234e7a43ee", + "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", "slot": 7 }, "source": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 } } @@ -268,7 +269,7 @@ ], "maxSlot": 7, "_info": { - "hash": "0x0ac3e9e19d138949b09835aa66cc38310122de679b0178b10443556aef6f5cfb", + "hash": "0xd90d9fbb57bfd2cc79cd04304f4cbc2686ed31e682a732dcf2ddd94e69e9d06b", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_slot_gaps_with_attestation_superseding[fork_Devnet]", "description": "Attestation superseding works correctly with missed slots.\n\n Scenario\n --------\n Process blocks at slots 1, 3, 5, 7 (skipping even slots).\n Proposers: validators 1, 3, 1, 3 (same validators repeat).\n\n Expected:\n - After slot 1: Validator 1 attests\n - After slot 3: Validator 3 attests, validator 1 moved to known\n - After slot 5: Validator 1 attests again (supersedes old), validator 3 in known\n - After slot 7: Validator 3 attests again (supersedes old), validator 1 in known\n\n Why This Matters\n ----------------\n Missed slots are normal when proposers fail to produce blocks.\n\n With non-contiguous slots, round-robin means validators propose multiple times.\n When they do, their newer attestations supersede their older ones.\n\n Total count stays at 2 (unique validators) throughout slots 5-7.\n\n This confirms attestation processing and superseding work correctly with slot gaps\n across both dictionaries.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json similarity index 71% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json index 54b7489..b6b86d5 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_advances_with_attestations[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_advances_with_attestations[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -77,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -90,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -116,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -129,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -155,8 +156,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -168,15 +169,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -194,8 +195,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -207,15 +208,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -233,8 +234,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -246,15 +247,15 @@ "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 } } @@ -264,7 +265,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0x7e18b05ce34ddd4a94e6a07a97fe1986d62b65252b964a5157999402b490acaa", + "hash": "0xb3a2ac78c3dca2def6826b97621593399629f80d66c5f6ed6838d6a8399110c5", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_advances_with_attestations[fork_Devnet]", "description": "Attestation target advances as attestation weight accumulates.\n\n Scenario\n --------\n Build a longer chain (slots 1-5) where attestations cause target advancement.\n\n Expected:\n - Initial blocks: target stays at genesis (slot 0)\n - Later blocks: target advances as attestations accumulate\n - Target remains behind head for safety\n\n Why This Matters\n ----------------\n As validators attest to blocks, the safe target advances, which in turn\n allows the attestation target to move forward.\n\n This demonstrates the dynamic nature of target selection: conservative initially,\n but advancing as consensus strengthens through attestation accumulation.\n\n The target advances only when sufficient attestation weight supports it.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json similarity index 73% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json index 8b7ea60..54fb982 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_at_genesis_initially[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_at_genesis_initially[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -77,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -90,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -116,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -129,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -147,7 +148,7 @@ ], "maxSlot": 2, "_info": { - "hash": "0x91defa7550c982000e57745a3b92d006fef483f005ad5ac0f7c0c5502d2a3760", + "hash": "0x55931a454acbfd50f0d0ef9f92395af85e7c37f0dcbb87251042c5d4c6e64f39", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_at_genesis_initially[fork_Devnet]", "description": "Attestation target starts at genesis before safe target updates.\n\n Scenario\n --------\n Process two blocks at slots 1 and 2.\n\n Expected:\n - After slot 1: target = slot 0 (genesis/finalized)\n - After slot 2: target = slot 0 (genesis/finalized)\n - Target root automatically validated against block at slot 0\n\n Why This Matters\n ----------------\n Initially, the safe target is at genesis (slot 0), so the attestation\n target walks back from head to genesis.\n\n This conservative behavior ensures validators don't attest too far ahead\n before there's sufficient attestation weight to advance the safe target.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json similarity index 68% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json index 48e3faa..ab3a6cf 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_justifiable_constraint[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_justifiable_constraint[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -77,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -90,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -116,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -129,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -155,8 +156,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -168,15 +169,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -194,8 +195,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -207,15 +208,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -233,8 +234,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -246,15 +247,15 @@ "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 } } @@ -272,8 +273,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", - "stateRoot": "0x3b523ede40789338d432b631a65579b3855c2861c90c44cf760e9e9bc6413cdc", + "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", "body": { "attestations": { "data": [] @@ -285,15 +286,15 @@ "data": { "slot": 6, "head": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "target": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "source": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 } } @@ -311,8 +312,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", - "stateRoot": "0xf1f50a275eee0692aa7c36132d2ee64ddecfa4f95c59627d723a4404a7ff4382", + "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", "body": { "attestations": { "data": [] @@ -324,15 +325,15 @@ "data": { "slot": 7, "head": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "target": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "source": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 } } @@ -350,8 +351,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", - "stateRoot": "0xa90ee765af23b009dff6abf61532da66fc61f2d42940ecca2d87cbfa5916e886", + "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", "body": { "attestations": { "data": [] @@ -363,15 +364,15 @@ "data": { "slot": 8, "head": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "target": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "source": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 } } @@ -389,8 +390,8 @@ "block": { "slot": 9, "proposerIndex": 1, - "parentRoot": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", - "stateRoot": "0x744b3ff0f2f4e429f580fdce378b10931435994228b4017d753823db3baf052e", + "parentRoot": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "stateRoot": "0xc8f2880bc8e3d1ba655c75f34ae5a20555503ce8296cbc3a2a266fbcdb7b078b", "body": { "attestations": { "data": [] @@ -402,15 +403,15 @@ "data": { "slot": 9, "head": { - "root": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", + "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", "slot": 9 }, "target": { - "root": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", + "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", "slot": 9 }, "source": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 } } @@ -428,8 +429,8 @@ "block": { "slot": 10, "proposerIndex": 2, - "parentRoot": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", - "stateRoot": "0x755bbab4584387db2837851ea71584ea97033a88703a899a08b2c8a263aa0659", + "parentRoot": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "stateRoot": "0x054571db4eb9706f6f29a97bffaa00765b2a75b77ad5812e8619066e942d3192", "body": { "attestations": { "data": [] @@ -441,15 +442,15 @@ "data": { "slot": 10, "head": { - "root": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", + "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", "slot": 10 }, "target": { - "root": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", + "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", "slot": 10 }, "source": { - "root": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", + "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", "slot": 9 } } @@ -467,8 +468,8 @@ "block": { "slot": 11, "proposerIndex": 3, - "parentRoot": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", - "stateRoot": "0x184cda538136a8062143a4045f35bfa114b60b15e00de3ed57994db03ce2c3b3", + "parentRoot": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "stateRoot": "0x4a359bc7767356dd13c761cc98bd9f0a62013636c0395e9babd5d37794c9c728", "body": { "attestations": { "data": [] @@ -480,15 +481,15 @@ "data": { "slot": 11, "head": { - "root": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", + "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", "slot": 11 }, "target": { - "root": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", + "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", "slot": 11 }, "source": { - "root": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", + "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", "slot": 10 } } @@ -506,8 +507,8 @@ "block": { "slot": 12, "proposerIndex": 0, - "parentRoot": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", - "stateRoot": "0x58d8a219d785385ea9b40a75442280464e8248444ee425537f4d6f4065dd35b2", + "parentRoot": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "stateRoot": "0x584b4ae5faaeb8a0cc6b190b4f570d773fc3f40426c0eb5a1f1730b199633b0a", "body": { "attestations": { "data": [] @@ -519,15 +520,15 @@ "data": { "slot": 12, "head": { - "root": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", + "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", "slot": 12 }, "target": { - "root": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", + "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", "slot": 12 }, "source": { - "root": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", + "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", "slot": 11 } } @@ -545,8 +546,8 @@ "block": { "slot": 13, "proposerIndex": 1, - "parentRoot": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", - "stateRoot": "0xf120a140fa1b513dccd0a69c7904098f0e32a7fdb23e9c1eea79cae231c29964", + "parentRoot": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "stateRoot": "0x4f3ed1bd4a625037ebbfdcf9b32a384ca5233b432df7e421f1d09af26d2bd841", "body": { "attestations": { "data": [] @@ -558,15 +559,15 @@ "data": { "slot": 13, "head": { - "root": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", + "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", "slot": 13 }, "target": { - "root": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", + "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", "slot": 13 }, "source": { - "root": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", + "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", "slot": 12 } } @@ -584,8 +585,8 @@ "block": { "slot": 14, "proposerIndex": 2, - "parentRoot": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", - "stateRoot": "0x6cda9c51f604bc376a0eb34dc70f9fadc6b4120e1702f607a769f9f71f4b1b8e", + "parentRoot": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "stateRoot": "0x638ae62be9308adae58e1239e365a27d053727985b6323652cb66c8a84926e88", "body": { "attestations": { "data": [] @@ -597,15 +598,15 @@ "data": { "slot": 14, "head": { - "root": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", + "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", "slot": 14 }, "target": { - "root": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", + "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", "slot": 14 }, "source": { - "root": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", + "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", "slot": 13 } } @@ -623,8 +624,8 @@ "block": { "slot": 15, "proposerIndex": 3, - "parentRoot": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", - "stateRoot": "0x816eedba680d484a5a4476e550f4be20411772b3abbe3281326ee6b8c96ca06b", + "parentRoot": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "stateRoot": "0x0792f723312433e078a11523609eb9b90a961d0ce1346717f86dfb56a09712ca", "body": { "attestations": { "data": [] @@ -636,15 +637,15 @@ "data": { "slot": 15, "head": { - "root": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", + "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", "slot": 15 }, "target": { - "root": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", + "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", "slot": 15 }, "source": { - "root": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", + "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", "slot": 14 } } @@ -662,8 +663,8 @@ "block": { "slot": 16, "proposerIndex": 0, - "parentRoot": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", - "stateRoot": "0xbd40ed0d00a8319c7bbc0b9170524733bb5cd6ef46b273dd09f41f90fc415d95", + "parentRoot": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "stateRoot": "0x76f71fb8340d28f209436d7f5a46d5ab47f6200ce78e98028869edcc17306c36", "body": { "attestations": { "data": [] @@ -675,15 +676,15 @@ "data": { "slot": 16, "head": { - "root": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", + "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", "slot": 16 }, "target": { - "root": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", + "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", "slot": 16 }, "source": { - "root": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", + "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", "slot": 15 } } @@ -701,8 +702,8 @@ "block": { "slot": 17, "proposerIndex": 1, - "parentRoot": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", - "stateRoot": "0xd1273f3339f0b990ce3ea696a73a99a0f027b679cd4fe42fd0a4addd3694327b", + "parentRoot": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "stateRoot": "0x73fb22f47f7645fe9c7484b48946bdcc9feefb77c89119cac4ebfbd897efdf05", "body": { "attestations": { "data": [] @@ -714,15 +715,15 @@ "data": { "slot": 17, "head": { - "root": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", + "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", "slot": 17 }, "target": { - "root": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", + "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", "slot": 17 }, "source": { - "root": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", + "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", "slot": 16 } } @@ -740,8 +741,8 @@ "block": { "slot": 18, "proposerIndex": 2, - "parentRoot": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", - "stateRoot": "0xbb6bf50f37821af5dbe71118e7f9644ebeff968760fa8dc33d3291710350e433", + "parentRoot": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "stateRoot": "0x304172e1836eede1f7a460cd0466152ea27449ced669a4e42133579f86d32640", "body": { "attestations": { "data": [] @@ -753,15 +754,15 @@ "data": { "slot": 18, "head": { - "root": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", + "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", "slot": 18 }, "target": { - "root": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", + "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", "slot": 18 }, "source": { - "root": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", + "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", "slot": 17 } } @@ -779,8 +780,8 @@ "block": { "slot": 19, "proposerIndex": 3, - "parentRoot": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", - "stateRoot": "0x595d5f28c6f0184f9c4768263fb735b5579806ea4881bcca3e7d04c4bb62dff5", + "parentRoot": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "stateRoot": "0xb0bfc653ee2bcbe154796b6f0449d89bb63b090444b361e9a86eaccc74898327", "body": { "attestations": { "data": [] @@ -792,15 +793,15 @@ "data": { "slot": 19, "head": { - "root": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", + "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", "slot": 19 }, "target": { - "root": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", + "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", "slot": 19 }, "source": { - "root": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", + "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", "slot": 18 } } @@ -818,8 +819,8 @@ "block": { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", - "stateRoot": "0x97640dc053b26e03933d4b601cdfbf3fae9022a9ead0c3ce5edef9c9af2a9214", + "parentRoot": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "stateRoot": "0x086dd2f62898dc60b1c7a964f30ee820c2fcb855c06aab1db1df6a7bce28690b", "body": { "attestations": { "data": [] @@ -831,15 +832,15 @@ "data": { "slot": 20, "head": { - "root": "0xc6d46c9b06b70aa612268373ff4a50e3aceac8623891ef06ce4fc63e13dfe323", + "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", "slot": 20 }, "target": { - "root": "0xc6d46c9b06b70aa612268373ff4a50e3aceac8623891ef06ce4fc63e13dfe323", + "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", "slot": 20 }, "source": { - "root": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", + "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", "slot": 19 } } @@ -857,8 +858,8 @@ "block": { "slot": 21, "proposerIndex": 1, - "parentRoot": "0xc6d46c9b06b70aa612268373ff4a50e3aceac8623891ef06ce4fc63e13dfe323", - "stateRoot": "0x0589ea9bd5917671b79af513b140e8e64c5f16f6b7f58ba7238d7d5fe8d6511e", + "parentRoot": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", + "stateRoot": "0xa1768c44b7eb89500ab88beae409d88b4978914cacbc1e38d9eaf628c9ddd7dd", "body": { "attestations": { "data": [] @@ -870,15 +871,15 @@ "data": { "slot": 21, "head": { - "root": "0x1624de01a0eb8983d1d99d6a78f3d88b23e83850e14f814df2b09a3fc1b9cae0", + "root": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", "slot": 21 }, "target": { - "root": "0x1624de01a0eb8983d1d99d6a78f3d88b23e83850e14f814df2b09a3fc1b9cae0", + "root": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", "slot": 21 }, "source": { - "root": "0xc6d46c9b06b70aa612268373ff4a50e3aceac8623891ef06ce4fc63e13dfe323", + "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", "slot": 20 } } @@ -896,8 +897,8 @@ "block": { "slot": 22, "proposerIndex": 2, - "parentRoot": "0x1624de01a0eb8983d1d99d6a78f3d88b23e83850e14f814df2b09a3fc1b9cae0", - "stateRoot": "0xca26cb314b942dfb6660bfba7d87b39ed2b562b9b9fd36ebf43a9b0fa0a6d121", + "parentRoot": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", + "stateRoot": "0x23d87e0515ea9b2a181553f65b199cce6fb9b99aac4072cef64bc46065f34f6c", "body": { "attestations": { "data": [] @@ -909,15 +910,15 @@ "data": { "slot": 22, "head": { - "root": "0xd432db9a45c3ba3fe2eab553c8e353281a900c823779a7a212f986a62631afff", + "root": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", "slot": 22 }, "target": { - "root": "0xd432db9a45c3ba3fe2eab553c8e353281a900c823779a7a212f986a62631afff", + "root": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", "slot": 22 }, "source": { - "root": "0x1624de01a0eb8983d1d99d6a78f3d88b23e83850e14f814df2b09a3fc1b9cae0", + "root": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", "slot": 21 } } @@ -935,8 +936,8 @@ "block": { "slot": 23, "proposerIndex": 3, - "parentRoot": "0xd432db9a45c3ba3fe2eab553c8e353281a900c823779a7a212f986a62631afff", - "stateRoot": "0xe209ba7f3e7f1c77ed9d590fbafbf3d59f7459c671642f2c39bbd44aab35069a", + "parentRoot": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", + "stateRoot": "0xd25fb61c44304889bd4c5601f3eee5303207970865fda537ccf0548adf3da872", "body": { "attestations": { "data": [] @@ -948,15 +949,15 @@ "data": { "slot": 23, "head": { - "root": "0xf99dac62a6c49e721e2d6f6b8bbabc620f8bca2aeec6f2cb0f1209fad8a9cc9b", + "root": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", "slot": 23 }, "target": { - "root": "0xf99dac62a6c49e721e2d6f6b8bbabc620f8bca2aeec6f2cb0f1209fad8a9cc9b", + "root": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", "slot": 23 }, "source": { - "root": "0xd432db9a45c3ba3fe2eab553c8e353281a900c823779a7a212f986a62631afff", + "root": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", "slot": 22 } } @@ -974,8 +975,8 @@ "block": { "slot": 24, "proposerIndex": 0, - "parentRoot": "0xf99dac62a6c49e721e2d6f6b8bbabc620f8bca2aeec6f2cb0f1209fad8a9cc9b", - "stateRoot": "0xec2ac6c8d8c54324666602aea0e885dd2dd546caf24a896a335e5cbca9abd557", + "parentRoot": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", + "stateRoot": "0x31b338e157a7afd26a7df8f081fef157a6b87eb41ac241fce2f5e82d96bdd94a", "body": { "attestations": { "data": [] @@ -987,15 +988,15 @@ "data": { "slot": 24, "head": { - "root": "0x30bc2f5168b1c743f9070dcadc4affd4be11c264c396cb842b7f0a9ced79fcaa", + "root": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", "slot": 24 }, "target": { - "root": "0x30bc2f5168b1c743f9070dcadc4affd4be11c264c396cb842b7f0a9ced79fcaa", + "root": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", "slot": 24 }, "source": { - "root": "0xf99dac62a6c49e721e2d6f6b8bbabc620f8bca2aeec6f2cb0f1209fad8a9cc9b", + "root": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", "slot": 23 } } @@ -1013,8 +1014,8 @@ "block": { "slot": 25, "proposerIndex": 1, - "parentRoot": "0x30bc2f5168b1c743f9070dcadc4affd4be11c264c396cb842b7f0a9ced79fcaa", - "stateRoot": "0xf3738e1236c5934350697b2494aa7a6d9218f669bcfe604e812598bb68ab58f3", + "parentRoot": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", + "stateRoot": "0x4838ec855755beaad6a75b042b07e625b711dc73f3db3f6a7aa143d78aa96fe7", "body": { "attestations": { "data": [] @@ -1026,15 +1027,15 @@ "data": { "slot": 25, "head": { - "root": "0x7ddd07b071cfd918513ab53d15ed431842d78813819fbe444475b2e6f27ef351", + "root": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", "slot": 25 }, "target": { - "root": "0x7ddd07b071cfd918513ab53d15ed431842d78813819fbe444475b2e6f27ef351", + "root": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", "slot": 25 }, "source": { - "root": "0x30bc2f5168b1c743f9070dcadc4affd4be11c264c396cb842b7f0a9ced79fcaa", + "root": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", "slot": 24 } } @@ -1052,8 +1053,8 @@ "block": { "slot": 26, "proposerIndex": 2, - "parentRoot": "0x7ddd07b071cfd918513ab53d15ed431842d78813819fbe444475b2e6f27ef351", - "stateRoot": "0xb6e92d72ac97a966b61207067a441201bdcc768f282ee4a05b06567794307b7b", + "parentRoot": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", + "stateRoot": "0x1116129ecbc1b85d65b48b8a94a3242d16c702be62f48737dc42003c937e20b9", "body": { "attestations": { "data": [] @@ -1065,15 +1066,15 @@ "data": { "slot": 26, "head": { - "root": "0xac2b4f7802b1436a62d747ecd1c9c452a6948bf77be79ba1d0d045544f92d7d1", + "root": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", "slot": 26 }, "target": { - "root": "0xac2b4f7802b1436a62d747ecd1c9c452a6948bf77be79ba1d0d045544f92d7d1", + "root": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", "slot": 26 }, "source": { - "root": "0x7ddd07b071cfd918513ab53d15ed431842d78813819fbe444475b2e6f27ef351", + "root": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", "slot": 25 } } @@ -1091,8 +1092,8 @@ "block": { "slot": 27, "proposerIndex": 3, - "parentRoot": "0xac2b4f7802b1436a62d747ecd1c9c452a6948bf77be79ba1d0d045544f92d7d1", - "stateRoot": "0x8b592d64d9d852b73ed9fa601d1d92747dea3d3d1f26092990883c0a26f1838a", + "parentRoot": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", + "stateRoot": "0x2457da3db79e0651bd7742fa8177ffb0b79337912b5aa1995d7f27137bfba52e", "body": { "attestations": { "data": [] @@ -1104,15 +1105,15 @@ "data": { "slot": 27, "head": { - "root": "0xfb9707b7f06403f767817cd0d19e1b589e7e8d9b25068093b6a49a6918c4afb6", + "root": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", "slot": 27 }, "target": { - "root": "0xfb9707b7f06403f767817cd0d19e1b589e7e8d9b25068093b6a49a6918c4afb6", + "root": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", "slot": 27 }, "source": { - "root": "0xac2b4f7802b1436a62d747ecd1c9c452a6948bf77be79ba1d0d045544f92d7d1", + "root": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", "slot": 26 } } @@ -1130,8 +1131,8 @@ "block": { "slot": 28, "proposerIndex": 0, - "parentRoot": "0xfb9707b7f06403f767817cd0d19e1b589e7e8d9b25068093b6a49a6918c4afb6", - "stateRoot": "0x5f48b869e4533c27181a4760b501c5994715cc7cc37f95febb017ff0d31b5a90", + "parentRoot": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", + "stateRoot": "0xd8593412bfbbf87faa4d6dcead949ab424ed592c51fe3b446013b880da50e72f", "body": { "attestations": { "data": [] @@ -1143,15 +1144,15 @@ "data": { "slot": 28, "head": { - "root": "0xc158ab7dfed3829bfdc6e906cbd5a396d3114209dd1c202378371bf09c0d638a", + "root": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", "slot": 28 }, "target": { - "root": "0xc158ab7dfed3829bfdc6e906cbd5a396d3114209dd1c202378371bf09c0d638a", + "root": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", "slot": 28 }, "source": { - "root": "0xfb9707b7f06403f767817cd0d19e1b589e7e8d9b25068093b6a49a6918c4afb6", + "root": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", "slot": 27 } } @@ -1169,8 +1170,8 @@ "block": { "slot": 29, "proposerIndex": 1, - "parentRoot": "0xc158ab7dfed3829bfdc6e906cbd5a396d3114209dd1c202378371bf09c0d638a", - "stateRoot": "0xe742dac236e4a270b4dd64f861e225c1452fa21f7773edd03b4d77610738b252", + "parentRoot": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", + "stateRoot": "0x77f20b52268782c3c117132e19c66e880a3fa137aadc78aa8cb37177a954a4da", "body": { "attestations": { "data": [] @@ -1182,15 +1183,15 @@ "data": { "slot": 29, "head": { - "root": "0xe4d91833748751f1c9c5311a31dc6fc9f90cfa9ccfb51f8526399e90c87bebe3", + "root": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", "slot": 29 }, "target": { - "root": "0xe4d91833748751f1c9c5311a31dc6fc9f90cfa9ccfb51f8526399e90c87bebe3", + "root": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", "slot": 29 }, "source": { - "root": "0xc158ab7dfed3829bfdc6e906cbd5a396d3114209dd1c202378371bf09c0d638a", + "root": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", "slot": 28 } } @@ -1208,8 +1209,8 @@ "block": { "slot": 30, "proposerIndex": 2, - "parentRoot": "0xe4d91833748751f1c9c5311a31dc6fc9f90cfa9ccfb51f8526399e90c87bebe3", - "stateRoot": "0x2baf88d0f454df0fdaaed5a64c8ce5135c7143272d2cd43b07b07c1f076556c2", + "parentRoot": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", + "stateRoot": "0xe953c85587e056ff27213e9f8bcebfc2fef4aa1351a9b09a87495b57055f4bf4", "body": { "attestations": { "data": [] @@ -1221,15 +1222,15 @@ "data": { "slot": 30, "head": { - "root": "0xb96cd256abc1e6c92ff48a03b26f34e44b9c3f115c2bbff0341866e21ffe552a", + "root": "0xdd596d1757fa73928246f105850f3777c7859f83ee0156ab5f53c47ea023c179", "slot": 30 }, "target": { - "root": "0xb96cd256abc1e6c92ff48a03b26f34e44b9c3f115c2bbff0341866e21ffe552a", + "root": "0xdd596d1757fa73928246f105850f3777c7859f83ee0156ab5f53c47ea023c179", "slot": 30 }, "source": { - "root": "0xe4d91833748751f1c9c5311a31dc6fc9f90cfa9ccfb51f8526399e90c87bebe3", + "root": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", "slot": 29 } } @@ -1239,7 +1240,7 @@ ], "maxSlot": 30, "_info": { - "hash": "0xf42040d03b640e5d3feba3d229fc7ea461d6d89ff25662fa5de961a52f968e76", + "hash": "0x087bb9195d9b77f7b684f8dcadd9c2b2db8f156eb9c7dd4853101aff46b5a42e", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_justifiable_constraint[fork_Devnet]", "description": "Attestation target advances while respecting justifiability rules.\n\n Scenario\n --------\n Build a 10-slot chain and observe how the attestation target advances\n over time while remaining justifiable relative to genesis (finalized at slot 0).\n\n Justifiability Rules (see Slot.is_justifiable_after)\n -----------------------------------------------------\n\n The target starts from current head and looks back at most 3 slots towards safe target.\n\n Then, a slot is deemed justifiable at distance delta from finalization if:\n 1. delta \u2264 5\n 2. delta is a perfect square (1, 4, 9, 16, 25, ...)\n 3. delta is a pronic number (2, 6, 12, 20, 30, ...)\n\n Why This Matters\n ----------------\n The justifiability rules prevent long-range attacks by restricting which\n checkpoints validators can attest to. The mathematical pattern (perfect squares\n and pronic numbers) creates increasingly sparse justifiable slots as the chain\n grows beyond finalization, providing security guarantees.\n\n The test verifies that the target selection algorithm respects these rules\n and never selects a non-justifiable target.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_with_extended_chain.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json similarity index 70% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_with_extended_chain.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json index 7b06e5c..39c7225 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_with_extended_chain.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_extended_chain[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_extended_chain[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -77,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -90,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -116,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -129,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -155,8 +156,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -168,15 +169,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -194,8 +195,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -207,15 +208,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -233,8 +234,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -246,15 +247,15 @@ "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 } } @@ -272,8 +273,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", - "stateRoot": "0x3b523ede40789338d432b631a65579b3855c2861c90c44cf760e9e9bc6413cdc", + "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", "body": { "attestations": { "data": [] @@ -285,15 +286,15 @@ "data": { "slot": 6, "head": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "target": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "source": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 } } @@ -311,8 +312,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", - "stateRoot": "0xf1f50a275eee0692aa7c36132d2ee64ddecfa4f95c59627d723a4404a7ff4382", + "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", "body": { "attestations": { "data": [] @@ -324,15 +325,15 @@ "data": { "slot": 7, "head": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "target": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "source": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 } } @@ -350,8 +351,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", - "stateRoot": "0xa90ee765af23b009dff6abf61532da66fc61f2d42940ecca2d87cbfa5916e886", + "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", "body": { "attestations": { "data": [] @@ -363,15 +364,15 @@ "data": { "slot": 8, "head": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "target": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "source": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 } } @@ -381,7 +382,7 @@ ], "maxSlot": 8, "_info": { - "hash": "0x544c68fd0c9eda1cf04c2c6e671eb53598a1dc47aba1cfee033f6af76ec7e201", + "hash": "0x32bba858f364641581f0b5fb55efa07e5f08f6e26e40673fad74bc4f52abd08e", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_extended_chain[fork_Devnet]", "description": "Attestation target advances progressively over extended chain.\n\n Scenario\n --------\n Build a longer chain (slots 1-8) observing target advancement pattern.\n\n Expected:\n - Initial slots: target at genesis (conservative)\n - Middle slots: target advances to slot 1\n - Target advances gradually, not jumping to head\n\n Why This Matters\n ----------------\n Over extended chains, the target selection should show smooth,\n gradual advancement as attestation weight accumulates.\n\n The target lags behind the head, providing a stable reference point that\n advances only when sufficient consensus has formed. This prevents validators\n from attesting too far ahead without adequate safety guarantees.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json similarity index 71% rename from lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json index d62a149..424e766 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_slot_gaps[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_slot_gaps[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -77,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -90,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -116,8 +117,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xcd17edc02a50383378ff96848805bbcea1b61b90a770da57d1114c584e8c6f19", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0x7031a4d5385dcfbe2a9f373845bb8ffd86863aed0d0d87976141c9b11edac5bc", "body": { "attestations": { "data": [] @@ -129,15 +130,15 @@ "data": { "slot": 3, "head": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 }, "target": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -155,8 +156,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", - "stateRoot": "0x9e0f5e90d9aa8238657d801bd6ca56fba6d503154fda163947d1de956593b38d", + "parentRoot": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "stateRoot": "0x1ee1961b8157e69e77990196fbab902d16c0f51bd8da1848dfa0a6d8e177ad7c", "body": { "attestations": { "data": [] @@ -168,15 +169,15 @@ "data": { "slot": 5, "head": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 }, "target": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 }, "source": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 } } @@ -186,7 +187,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0x216e8ddfbd753ece6fe736c3aa752b8dd5e74d685764b989d43fce51f44b752c", + "hash": "0x601d371d2e0b5167a607bdf93cd3a2559623711e09ab8d8d7f17afdfd51cb991", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_slot_gaps[fork_Devnet]", "description": "Attestation target handles missed slots correctly.\n\n Scenario\n --------\n Process blocks at slots 1, 3, 5 (skipping even slots).\n\n Expected:\n - Targets advance despite gaps\n - Targets remain justifiable\n - Safe target stays valid\n\n Why This Matters\n ----------------\n Missed slots are common when proposers fail or network partitions occur.\n\n The target selection must handle sparse block production gracefully,\n ensuring validators can still make progress even with gaps in the chain.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_advances_through_deep_chain.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json similarity index 67% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_advances_through_deep_chain.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json index cbb27c5..5d5d2a7 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_advances_through_deep_chain.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_advances_through_deep_chain[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_advances_through_deep_chain[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -76,8 +77,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -89,15 +90,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -114,8 +115,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -127,15 +128,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,8 +153,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -165,15 +166,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -190,8 +191,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -203,15 +204,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -228,8 +229,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -241,15 +242,15 @@ "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 } } @@ -266,8 +267,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", - "stateRoot": "0x3b523ede40789338d432b631a65579b3855c2861c90c44cf760e9e9bc6413cdc", + "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", "body": { "attestations": { "data": [] @@ -279,15 +280,15 @@ "data": { "slot": 6, "head": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "target": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 }, "source": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 } } @@ -304,8 +305,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", - "stateRoot": "0xf1f50a275eee0692aa7c36132d2ee64ddecfa4f95c59627d723a4404a7ff4382", + "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", "body": { "attestations": { "data": [] @@ -317,15 +318,15 @@ "data": { "slot": 7, "head": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "target": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 }, "source": { - "root": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", + "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", "slot": 6 } } @@ -342,8 +343,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", - "stateRoot": "0xa90ee765af23b009dff6abf61532da66fc61f2d42940ecca2d87cbfa5916e886", + "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", "body": { "attestations": { "data": [] @@ -355,15 +356,15 @@ "data": { "slot": 8, "head": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "target": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 }, "source": { - "root": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", + "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", "slot": 7 } } @@ -380,8 +381,8 @@ "block": { "slot": 9, "proposerIndex": 1, - "parentRoot": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", - "stateRoot": "0x744b3ff0f2f4e429f580fdce378b10931435994228b4017d753823db3baf052e", + "parentRoot": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "stateRoot": "0xc8f2880bc8e3d1ba655c75f34ae5a20555503ce8296cbc3a2a266fbcdb7b078b", "body": { "attestations": { "data": [] @@ -393,15 +394,15 @@ "data": { "slot": 9, "head": { - "root": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", + "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", "slot": 9 }, "target": { - "root": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", + "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", "slot": 9 }, "source": { - "root": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", + "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", "slot": 8 } } @@ -418,8 +419,8 @@ "block": { "slot": 10, "proposerIndex": 2, - "parentRoot": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", - "stateRoot": "0x755bbab4584387db2837851ea71584ea97033a88703a899a08b2c8a263aa0659", + "parentRoot": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "stateRoot": "0x054571db4eb9706f6f29a97bffaa00765b2a75b77ad5812e8619066e942d3192", "body": { "attestations": { "data": [] @@ -431,15 +432,15 @@ "data": { "slot": 10, "head": { - "root": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", + "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", "slot": 10 }, "target": { - "root": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", + "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", "slot": 10 }, "source": { - "root": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", + "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", "slot": 9 } } @@ -456,8 +457,8 @@ "block": { "slot": 11, "proposerIndex": 3, - "parentRoot": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", - "stateRoot": "0x184cda538136a8062143a4045f35bfa114b60b15e00de3ed57994db03ce2c3b3", + "parentRoot": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "stateRoot": "0x4a359bc7767356dd13c761cc98bd9f0a62013636c0395e9babd5d37794c9c728", "body": { "attestations": { "data": [] @@ -469,15 +470,15 @@ "data": { "slot": 11, "head": { - "root": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", + "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", "slot": 11 }, "target": { - "root": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", + "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", "slot": 11 }, "source": { - "root": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", + "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", "slot": 10 } } @@ -494,8 +495,8 @@ "block": { "slot": 12, "proposerIndex": 0, - "parentRoot": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", - "stateRoot": "0x58d8a219d785385ea9b40a75442280464e8248444ee425537f4d6f4065dd35b2", + "parentRoot": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "stateRoot": "0x584b4ae5faaeb8a0cc6b190b4f570d773fc3f40426c0eb5a1f1730b199633b0a", "body": { "attestations": { "data": [] @@ -507,15 +508,15 @@ "data": { "slot": 12, "head": { - "root": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", + "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", "slot": 12 }, "target": { - "root": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", + "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", "slot": 12 }, "source": { - "root": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", + "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", "slot": 11 } } @@ -532,8 +533,8 @@ "block": { "slot": 13, "proposerIndex": 1, - "parentRoot": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", - "stateRoot": "0xf120a140fa1b513dccd0a69c7904098f0e32a7fdb23e9c1eea79cae231c29964", + "parentRoot": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "stateRoot": "0x4f3ed1bd4a625037ebbfdcf9b32a384ca5233b432df7e421f1d09af26d2bd841", "body": { "attestations": { "data": [] @@ -545,15 +546,15 @@ "data": { "slot": 13, "head": { - "root": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", + "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", "slot": 13 }, "target": { - "root": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", + "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", "slot": 13 }, "source": { - "root": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", + "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", "slot": 12 } } @@ -570,8 +571,8 @@ "block": { "slot": 14, "proposerIndex": 2, - "parentRoot": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", - "stateRoot": "0x6cda9c51f604bc376a0eb34dc70f9fadc6b4120e1702f607a769f9f71f4b1b8e", + "parentRoot": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "stateRoot": "0x638ae62be9308adae58e1239e365a27d053727985b6323652cb66c8a84926e88", "body": { "attestations": { "data": [] @@ -583,15 +584,15 @@ "data": { "slot": 14, "head": { - "root": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", + "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", "slot": 14 }, "target": { - "root": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", + "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", "slot": 14 }, "source": { - "root": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", + "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", "slot": 13 } } @@ -608,8 +609,8 @@ "block": { "slot": 15, "proposerIndex": 3, - "parentRoot": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", - "stateRoot": "0x816eedba680d484a5a4476e550f4be20411772b3abbe3281326ee6b8c96ca06b", + "parentRoot": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "stateRoot": "0x0792f723312433e078a11523609eb9b90a961d0ce1346717f86dfb56a09712ca", "body": { "attestations": { "data": [] @@ -621,15 +622,15 @@ "data": { "slot": 15, "head": { - "root": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", + "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", "slot": 15 }, "target": { - "root": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", + "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", "slot": 15 }, "source": { - "root": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", + "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", "slot": 14 } } @@ -646,8 +647,8 @@ "block": { "slot": 16, "proposerIndex": 0, - "parentRoot": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", - "stateRoot": "0xbd40ed0d00a8319c7bbc0b9170524733bb5cd6ef46b273dd09f41f90fc415d95", + "parentRoot": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "stateRoot": "0x76f71fb8340d28f209436d7f5a46d5ab47f6200ce78e98028869edcc17306c36", "body": { "attestations": { "data": [] @@ -659,15 +660,15 @@ "data": { "slot": 16, "head": { - "root": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", + "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", "slot": 16 }, "target": { - "root": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", + "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", "slot": 16 }, "source": { - "root": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", + "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", "slot": 15 } } @@ -684,8 +685,8 @@ "block": { "slot": 17, "proposerIndex": 1, - "parentRoot": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", - "stateRoot": "0xd1273f3339f0b990ce3ea696a73a99a0f027b679cd4fe42fd0a4addd3694327b", + "parentRoot": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "stateRoot": "0x73fb22f47f7645fe9c7484b48946bdcc9feefb77c89119cac4ebfbd897efdf05", "body": { "attestations": { "data": [] @@ -697,15 +698,15 @@ "data": { "slot": 17, "head": { - "root": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", + "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", "slot": 17 }, "target": { - "root": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", + "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", "slot": 17 }, "source": { - "root": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", + "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", "slot": 16 } } @@ -722,8 +723,8 @@ "block": { "slot": 18, "proposerIndex": 2, - "parentRoot": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", - "stateRoot": "0xbb6bf50f37821af5dbe71118e7f9644ebeff968760fa8dc33d3291710350e433", + "parentRoot": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "stateRoot": "0x304172e1836eede1f7a460cd0466152ea27449ced669a4e42133579f86d32640", "body": { "attestations": { "data": [] @@ -735,15 +736,15 @@ "data": { "slot": 18, "head": { - "root": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", + "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", "slot": 18 }, "target": { - "root": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", + "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", "slot": 18 }, "source": { - "root": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", + "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", "slot": 17 } } @@ -760,8 +761,8 @@ "block": { "slot": 19, "proposerIndex": 3, - "parentRoot": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", - "stateRoot": "0x595d5f28c6f0184f9c4768263fb735b5579806ea4881bcca3e7d04c4bb62dff5", + "parentRoot": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "stateRoot": "0xb0bfc653ee2bcbe154796b6f0449d89bb63b090444b361e9a86eaccc74898327", "body": { "attestations": { "data": [] @@ -773,15 +774,15 @@ "data": { "slot": 19, "head": { - "root": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", + "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", "slot": 19 }, "target": { - "root": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", + "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", "slot": 19 }, "source": { - "root": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", + "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", "slot": 18 } } @@ -792,7 +793,7 @@ "valid": true, "checks": { "headSlot": 20, - "headRoot": "0xc6d46c9b06b70aa612268373ff4a50e3aceac8623891ef06ce4fc63e13dfe323", + "headRoot": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", "headRootLabel": "block_20" }, "stepType": "block", @@ -800,8 +801,8 @@ "block": { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", - "stateRoot": "0x97640dc053b26e03933d4b601cdfbf3fae9022a9ead0c3ce5edef9c9af2a9214", + "parentRoot": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "stateRoot": "0x086dd2f62898dc60b1c7a964f30ee820c2fcb855c06aab1db1df6a7bce28690b", "body": { "attestations": { "data": [] @@ -813,15 +814,15 @@ "data": { "slot": 20, "head": { - "root": "0xc6d46c9b06b70aa612268373ff4a50e3aceac8623891ef06ce4fc63e13dfe323", + "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", "slot": 20 }, "target": { - "root": "0xc6d46c9b06b70aa612268373ff4a50e3aceac8623891ef06ce4fc63e13dfe323", + "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", "slot": 20 }, "source": { - "root": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", + "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", "slot": 19 } } @@ -832,7 +833,7 @@ ], "maxSlot": 20, "_info": { - "hash": "0x02661648b6c9819ed5a88c85135cc61c470384dff50c97f4002ae4fbb7dff55c", + "hash": "0x5ad4414ae62a84ccc1d0b107e7305210bfef592d54e97ea73cf07fc40d551e9e", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_advances_through_deep_chain[fork_Devnet]", "description": "Fork choice head advances through a deep chain correctly.\n\n Scenario\n --------\n Build a long chain (slots 1-20) and verify head reaches the end.\n\n Expected Behavior:\n - Head advances through all 20 blocks\n - Final head = slot 20\n - Fork choice scales to longer chains\n\n Why This Matters\n ----------------\n This tests that the fork choice algorithm scales to longer chains and\n correctly handles the tree-walking logic through many blocks.\n\n Real networks have chains thousands of blocks long. The algorithm must:\n - Efficiently traverse deep trees\n - Maintain correct head even with many ancestors\n - Not degrade in performance or correctness with depth\n\n A 20-block chain is a modest test of this scalability.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_switches_to_heavier_fork.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json similarity index 70% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_switches_to_heavier_fork.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json index 17f8513..b014d09 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_switches_to_heavier_fork.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_switches_to_heavier_fork[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_switches_to_heavier_fork[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -70,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "headRootLabel": "common" }, "stepType": "block", @@ -78,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -91,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -111,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a" }, "stepType": "block", @@ -119,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -132,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a" }, "stepType": "block", @@ -160,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -173,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -193,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -201,8 +202,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -214,15 +215,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -233,7 +234,7 @@ ], "maxSlot": 3, "_info": { - "hash": "0xc47ce6155615b82e59212a2886a88cdc3f883200ae576081bda7abdf9545c21f", + "hash": "0x3cd680ce8974c9c4bfaefeeb90632310c9c67099d2f8eba5a0b04addccdd4c43", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_switches_to_heavier_fork[fork_Devnet]", "description": "Fork choice head switches when a competing fork becomes heavier.\n\n Scenario\n --------\n Create two forks at slot 2, then extend one fork to make it heavier.\n\n Expected Behavior:\n - After fork A (slot 2): head = fork A\n - After fork B (slot 2): head = still fork A (tie-breaker)\n - After extending fork B (slot 3): head = slot 3 (fork B wins!)\n\n Why This Matters\n ----------------\n This demonstrates the core LMD-GHOST property: the head follows the heaviest\n subtree. When fork B is extended with a child block, that child's proposer\n implicitly attests to fork B, giving it more weight.\n\n Fork choice recognizes this weight increase and switches the head to fork B's\n descendant. This is how the protocol reaches consensus - validators converge\n on the fork with the most support (weight).\n\n This is also how reorgs happen: a previously non-canonical fork can become\n canonical if it gains more attestation weight.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_deep_fork_split.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json similarity index 67% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_deep_fork_split.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json index f02a1c8..af448e2 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_deep_fork_split.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_deep_fork_split[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_deep_fork_split[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -70,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "headRootLabel": "common" }, "stepType": "block", @@ -78,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -91,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -111,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -119,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -132,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,7 +153,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -160,8 +161,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -173,15 +174,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -193,7 +194,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -201,8 +202,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -214,15 +215,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -234,7 +235,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -242,8 +243,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -255,15 +256,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -275,7 +276,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -283,8 +284,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -296,15 +297,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -316,7 +317,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -324,8 +325,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -337,15 +338,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -357,7 +358,7 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "headRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "headRootLabel": "fork_b_5" }, "stepType": "block", @@ -365,8 +366,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -378,15 +379,15 @@ "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 } } @@ -397,7 +398,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0xa88fb733e4bfeb4d33071059a6d0cfcf83e339941c6401a9fd9fca6dc7911a47", + "hash": "0x6ae81fc5748a34ca376abfec596672a8dbc915b10affa5662bb555b4ca957d15", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_deep_fork_split[fork_Devnet]", "description": "Fork choice handles deep fork splits correctly.\n\n Scenario\n --------\n Create two forks that diverge at slot 2 and extend to different depths.\n\n Expected Behavior:\n - Fork A extends to slot 4\n - Fork B extends to slot 5\n - Head follows the longer (heavier) fork B\n\n Why This Matters\n ----------------\n In practice, forks can persist for multiple slots before one gains dominance.\n This tests that fork choice correctly follows the deeper fork, which has\n accumulated more proposer attestations along its chain.\n\n Each block in a fork adds weight from its proposer's attestation. A longer\n fork has more accumulated weight from the proposers along its length.\n\n This is how the protocol ensures liveness: the chain that continues to grow\n (accumulating blocks and attestations) becomes the canonical chain.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_gaps_in_slots.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json similarity index 70% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_gaps_in_slots.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json index 647b74e..08f0fba 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_gaps_in_slots.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_gaps_in_slots[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_gaps_in_slots[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -76,8 +77,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -89,15 +90,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -114,8 +115,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xcd17edc02a50383378ff96848805bbcea1b61b90a770da57d1114c584e8c6f19", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0x7031a4d5385dcfbe2a9f373845bb8ffd86863aed0d0d87976141c9b11edac5bc", "body": { "attestations": { "data": [] @@ -127,15 +128,15 @@ "data": { "slot": 3, "head": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 }, "target": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,8 +153,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", - "stateRoot": "0x9e0f5e90d9aa8238657d801bd6ca56fba6d503154fda163947d1de956593b38d", + "parentRoot": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "stateRoot": "0x1ee1961b8157e69e77990196fbab902d16c0f51bd8da1848dfa0a6d8e177ad7c", "body": { "attestations": { "data": [] @@ -165,15 +166,15 @@ "data": { "slot": 5, "head": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 }, "target": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 }, "source": { - "root": "0x117813f4703a060bf63d618c8707b226718270ea27f7a532326bb5a9f96de79f", + "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", "slot": 3 } } @@ -190,8 +191,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", - "stateRoot": "0xcc162a538e469351494e5d7e5e73ad35e73e847b8ad8d3ab98354ebc0897cde9", + "parentRoot": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "stateRoot": "0x784f0f32d518e8a5ccdc7b0887d26d64b2d0048faa696c0616d8ff5a37d02d28", "body": { "attestations": { "data": [] @@ -203,15 +204,15 @@ "data": { "slot": 7, "head": { - "root": "0x550c43967f27c9b0dfe97cdebc8dcfc392b34e18108f2ab012dce9234e7a43ee", + "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", "slot": 7 }, "target": { - "root": "0x550c43967f27c9b0dfe97cdebc8dcfc392b34e18108f2ab012dce9234e7a43ee", + "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", "slot": 7 }, "source": { - "root": "0x26665fbe4975fee233c3b72edf9be5a7b894f6371a05964ad2cee586502d8b2f", + "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", "slot": 5 } } @@ -228,8 +229,8 @@ "block": { "slot": 9, "proposerIndex": 1, - "parentRoot": "0x550c43967f27c9b0dfe97cdebc8dcfc392b34e18108f2ab012dce9234e7a43ee", - "stateRoot": "0x9f6f5883ec87ff9b2acf3e6fd126ebf353b33372270c9547a4acdf16484cc25f", + "parentRoot": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", + "stateRoot": "0x2b033d926f0beb3031e0abc960a396f6e4ba10daa94a8b8598fd01a8f2b8c36d", "body": { "attestations": { "data": [] @@ -241,15 +242,15 @@ "data": { "slot": 9, "head": { - "root": "0x139d53be0248a72c06382b3da3b2abc93227d25c8b1d8a01aa86e19dafb9ff96", + "root": "0x70f7a111cd22263e3e02b323dbc47d9c67961ad39159d93a4ed0e4c5efb5b5e3", "slot": 9 }, "target": { - "root": "0x139d53be0248a72c06382b3da3b2abc93227d25c8b1d8a01aa86e19dafb9ff96", + "root": "0x70f7a111cd22263e3e02b323dbc47d9c67961ad39159d93a4ed0e4c5efb5b5e3", "slot": 9 }, "source": { - "root": "0x550c43967f27c9b0dfe97cdebc8dcfc392b34e18108f2ab012dce9234e7a43ee", + "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", "slot": 7 } } @@ -259,7 +260,7 @@ ], "maxSlot": 9, "_info": { - "hash": "0x8b81d7ba5704d1e3814b6b30e8d78607637d73d3d22943bfe1a8cfe6acc88649", + "hash": "0x9e626b69dcb3336fcc73a16b988aff4a08d107fc0b9542cf0512c7b51bbd7231", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_gaps_in_slots[fork_Devnet]", "description": "Fork choice head handles missing slots correctly.\n\n Scenario\n --------\n Build blocks at slots 1, 3, 5, 7, 9 (skipping even slots).\n\n Expected Behavior:\n - Head advances to each present block\n - Skipped slots don't affect fork choice\n - Head correctly identifies the leaf despite gaps\n\n Why This Matters\n ----------------\n Missed slots are common in production:\n - Offline proposers\n - Network partitions\n - Proposer failures\n\n Fork choice must handle sparse block production correctly. The algorithm\n doesn't require consecutive slots - it works with any tree structure where\n gaps are simply missing nodes.\n\n This verifies the algorithm handles real-world conditions where not every\n slot has a block, which is the norm rather than the exception.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_large_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json similarity index 71% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_large_gaps.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json index fb32c1e..54da480 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_large_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_large_gaps[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_large_gaps[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -76,8 +77,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -89,15 +90,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -114,8 +115,8 @@ "block": { "slot": 10, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0x544cbec1f19f85e6fe3559733d16506691f37acf5ddaeeb0105df5364c836c01", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0x1b3e2d27ccd32951999bb5859116a897a055f096276d78601158e22fd2632442", "body": { "attestations": { "data": [] @@ -127,15 +128,15 @@ "data": { "slot": 10, "head": { - "root": "0x600356e9bb5fa437a2269f86df953b33d78c0cd1f178392e0562eead5d89b832", + "root": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", "slot": 10 }, "target": { - "root": "0x600356e9bb5fa437a2269f86df953b33d78c0cd1f178392e0562eead5d89b832", + "root": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", "slot": 10 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,8 +153,8 @@ "block": { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x600356e9bb5fa437a2269f86df953b33d78c0cd1f178392e0562eead5d89b832", - "stateRoot": "0x117342afd01161820354413cc88ee1352cf961b3a1a7cab5ead074e38a39ce85", + "parentRoot": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", + "stateRoot": "0x4918471d37cd4049787f147e83fc9aab2c80a492aba13460a4cde889e301954e", "body": { "attestations": { "data": [] @@ -165,15 +166,15 @@ "data": { "slot": 20, "head": { - "root": "0x344e5a9c8bf1e87293bb291f9099d8075f1162fdb91290cb8534cdf30bc3a39e", + "root": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", "slot": 20 }, "target": { - "root": "0x344e5a9c8bf1e87293bb291f9099d8075f1162fdb91290cb8534cdf30bc3a39e", + "root": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", "slot": 20 }, "source": { - "root": "0x600356e9bb5fa437a2269f86df953b33d78c0cd1f178392e0562eead5d89b832", + "root": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", "slot": 10 } } @@ -190,8 +191,8 @@ "block": { "slot": 30, "proposerIndex": 2, - "parentRoot": "0x344e5a9c8bf1e87293bb291f9099d8075f1162fdb91290cb8534cdf30bc3a39e", - "stateRoot": "0xe93e75ceeb6fa60fdf49e6ae395b9e95bf5e6ec1d4476ba2a38488effa07933b", + "parentRoot": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", + "stateRoot": "0x2cd0120e789b59cb2e0a8ac5204249b289898918714582cc034686cb75801d22", "body": { "attestations": { "data": [] @@ -203,15 +204,15 @@ "data": { "slot": 30, "head": { - "root": "0xd2a428fe19b7cedd97d2308b44089da17669e56324cdf3668f6e84eee223e39a", + "root": "0x1a6568b59c71bea3297711437ce946b615837efcf65443286ec3a3cbcddc83c3", "slot": 30 }, "target": { - "root": "0xd2a428fe19b7cedd97d2308b44089da17669e56324cdf3668f6e84eee223e39a", + "root": "0x1a6568b59c71bea3297711437ce946b615837efcf65443286ec3a3cbcddc83c3", "slot": 30 }, "source": { - "root": "0x344e5a9c8bf1e87293bb291f9099d8075f1162fdb91290cb8534cdf30bc3a39e", + "root": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", "slot": 20 } } @@ -221,7 +222,7 @@ ], "maxSlot": 30, "_info": { - "hash": "0xfeff16518d03ab1abb2fe08ad244cd49952ab8031e77c5d52f8a9fae3ec87c29", + "hash": "0x75234ae4514adf94fbfd2f336070fdcbd98111f59920b4ada865446b8fd6d008", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_large_gaps[fork_Devnet]", "description": "Fork choice head handles large gaps between blocks.\n\n Scenario\n --------\n Build blocks at slots 1, 10, 20, 30 (gaps of 9-10 slots).\n\n Expected Behavior:\n - Head advances despite large gaps\n - Fork choice is gap-size independent\n - Head reaches the furthest block\n\n Why This Matters\n ----------------\n Large gaps can occur during:\n - Extended network partitions\n - Chain reorganizations\n - Periods of high validator downtime\n - Initial sync after being offline\n\n The fork choice algorithm must remain correct regardless of gap size.\n Distance between blocks should not affect the correctness of head selection -\n only the tree structure matters.\n\n This test verifies that even with dramatic gaps (representing severe network\n conditions), fork choice still identifies the correct head.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_two_competing_forks.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json similarity index 71% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_two_competing_forks.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json index be8508e..69184af 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_head/test_head_with_two_competing_forks.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_two_competing_forks[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_two_competing_forks[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -70,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "headRootLabel": "common" }, "stepType": "block", @@ -78,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -91,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -111,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a" }, "stepType": "block", @@ -119,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -132,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a" }, "stepType": "block", @@ -160,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -173,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -192,7 +193,7 @@ ], "maxSlot": 2, "_info": { - "hash": "0x128138a9f96ae9f3d2f73514a048123ecb8b3e433ce3e2f3674f3582b5dc9092", + "hash": "0x445ea1d1d46f1a51804be76a2127589d3af23623d2105b4cfbf13d94032c85b8", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_two_competing_forks[fork_Devnet]", "description": "Fork choice selects head when two forks compete at the same slot.\n\n Scenario\n --------\n Create two competing blocks at slot 2, both building on slot 1.\n\n Expected Behavior:\n - After slot 1: head = slot 1 (common ancestor)\n - After fork A (slot 2): head = slot 2 (fork A, first seen)\n - After fork B (slot 2): head = slot 2 (still fork A)\n - Both forks have equal weight (1 proposer attestation each)\n - Head breaks tie lexicographically by block root\n\n Why This Matters\n ----------------\n This is an important fork choice scenario: two blocks competing for the\n same slot. Even with equal attestation weight, fork choice must deterministically\n select a head.\n\n The algorithm uses lexicographic order of block roots as a tie-breaker,\n ensuring all nodes agree on the same head even when forks have equal weight.\n\n This prevents network splits and ensures consensus converges.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json similarity index 58% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json index 29a9697..b38a880 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_back_and_forth_reorg_oscillation[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_back_and_forth_reorg_oscillation[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,20 +31,28 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 + }, + { + "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "index": 4 + }, + { + "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "index": 5 } ] }, @@ -58,7 +67,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe483b5237726c89bec17cd6aa4dd397be5b4952d73e7e79d4c4c40b88734227e", "body": { "attestations": { "data": [] @@ -70,7 +79,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "headRootLabel": "base" }, "stepType": "block", @@ -78,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", + "stateRoot": "0x6f64b6379a51d5ea3a51eee1dde0e2457e89942177a60a0705ad295acadf7674", "body": { "attestations": { "data": [] @@ -91,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", "slot": 0 } } @@ -111,7 +120,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -119,8 +128,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", "body": { "attestations": { "data": [] @@ -132,15 +141,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 } } @@ -152,7 +161,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -160,8 +169,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", "body": { "attestations": { "data": [] @@ -173,15 +182,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 } } @@ -193,7 +202,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -201,8 +210,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", "body": { "attestations": { "data": [] @@ -214,15 +223,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 } } @@ -234,7 +243,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -242,8 +251,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", "body": { "attestations": { "data": [] @@ -255,15 +264,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 } } @@ -275,16 +284,16 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "headRootLabel": "fork_a_4" }, "stepType": "block", "block": { "block": { "slot": 4, - "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "proposerIndex": 4, + "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", "body": { "attestations": { "data": [] @@ -292,19 +301,19 @@ } }, "proposerAttestation": { - "validatorId": 0, + "validatorId": 4, "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 } } @@ -316,16 +325,16 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "headRootLabel": "fork_a_4" }, "stepType": "block", "block": { "block": { "slot": 4, - "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "proposerIndex": 4, + "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", "body": { "attestations": { "data": [] @@ -333,19 +342,19 @@ } }, "proposerAttestation": { - "validatorId": 0, + "validatorId": 4, "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 } } @@ -357,16 +366,16 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "headRoot": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", "headRootLabel": "fork_b_5" }, "stepType": "block", "block": { "block": { "slot": 5, - "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "proposerIndex": 5, + "parentRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "stateRoot": "0xa2b05e5ab5824c023fcfce5f1e9276d95c2c034ed07721d059d5ad2c07007d99", "body": { "attestations": { "data": [] @@ -374,19 +383,19 @@ } }, "proposerAttestation": { - "validatorId": 1, + "validatorId": 5, "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 } } @@ -397,10 +406,10 @@ ], "maxSlot": 5, "_info": { - "hash": "0x3be19dff1b20d9aacb1193170e3dd1219dbfbf0398fd9e5de3a42e298735d2f1", + "hash": "0x6070115be326d9aaf595d26edf621384bbdfc35b4bf5430010c2472ed3064e65", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_back_and_forth_reorg_oscillation[fork_Devnet]", - "description": "Multiple reorgs as two forks alternately extend (pathological case).\n\n Scenario\n --------\n Two forks alternate extensions, causing head to oscillate back and forth.\n This is a pathological case that shouldn't happen in healthy networks but\n tests fork choice correctness under extreme conditions.\n\n Oscillation Pattern:\n Slot 2: Fork A leads (1 block) \u2190 head\n Slot 3: Fork B catches up (1 block each) \u2192 tie\n Slot 4: Fork B extends (2 vs 1) \u2190 head switches to B\n Slot 5: Fork A extends (2 vs 2) \u2192 tie\n Slot 6: Fork A extends (3 vs 2) \u2190 head switches to A\n Slot 7: Fork B extends (3 vs 3) \u2192 tie\n Slot 8: Fork B extends (4 vs 3) \u2190 head switches to B\n\n Expected Behavior\n -----------------\n 1. Head oscillates: A \u2192 B \u2192 A \u2192 B\n 2. Each extension triggers reorg to that fork\n 3. All reorgs are 1-2 blocks deep\n 4. Fork choice remains consistent and correct throughout\n\n Reorg Count: 3 reorgs in 6 slots (very high rate)\n\n Why This Matters\n ----------------\n While extremely rare, this scenario can theoretically occur:\n - Two validator groups in different network segments\n - Each group primarily seeing their own fork first\n - Alternating proposer selection between groups\n - High network latency preventing convergence\n\n Properties Tested:\n - Fork choice handles rapid reorg sequences\n - No state corruption despite frequent head changes\n - Tie-breaking remains consistent\n - Weight calculation correct after multiple reorgs\n - System eventually stabilizes to heaviest fork\n\n This stress test verifies robustness under worst-case fork competition,\n ensuring the protocol remains safe even in pathological network conditions.\n In practice, networks self-heal from such scenarios through attestation\n convergence.", + "description": "Multiple reorgs as two forks alternately extend (pathological case).\n\n Scenario\n --------\n Two forks alternate extensions, causing head to oscillate back and forth.\n This is a pathological case that shouldn't happen in healthy networks but\n tests fork choice correctness under extreme conditions.\n\n Oscillation Pattern:\n Slot 2: Fork A leads (1 vs 0) \u2190 head\n Slot 2: Fork B created (1 vs 1) \u2192 tie, A maintains\n Slot 3: Fork B extends (2 vs 1) \u2190 head switches to B (REORG #1)\n Slot 3: Fork A extends (2 vs 2) \u2192 tie, B maintains\n Slot 4: Fork A extends (3 vs 2) \u2190 head switches to A (REORG #2)\n Slot 4: Fork B extends (3 vs 3) \u2192 tie, A maintains\n Slot 5: Fork B extends (4 vs 3) \u2190 head switches to B (REORG #3)\n\n Expected Behavior\n -----------------\n 1. Head oscillates: A \u2192 B \u2192 A \u2192 B\n 2. Each extension triggers reorg to that fork\n 3. All reorgs are 1-2 blocks deep\n 4. Fork choice remains consistent and correct throughout\n\n Reorg Count: 3 reorgs in 4 slots (very high rate)\n\n Why This Matters\n ----------------\n While extremely rare, this scenario can theoretically occur:\n - Two validator groups in different network segments\n - Each group primarily seeing their own fork first\n - Alternating proposer selection between groups\n - High network latency preventing convergence\n\n Properties Tested:\n - Fork choice handles rapid reorg sequences\n - No state corruption despite frequent head changes\n - Tie-breaking remains consistent\n - Weight calculation correct after multiple reorgs\n - System eventually stabilizes to heaviest fork\n\n This stress test verifies robustness under worst-case fork competition,\n ensuring the protocol remains safe even in pathological network conditions.\n In practice, networks self-heal from such scenarios through attestation\n convergence.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json similarity index 50% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json index 6e643f3..a0bbab1 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_on_newly_justified_slot[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_on_newly_justified_slot[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,39 +31,39 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 }, { - "pubkey": "0xe9c00205c556b91d56cb8e5bd9427b3a3bd5b32aef243616c8af133b9a9b6f37c428ce3870fd3b231c960e26785cb3109e0fc115", + "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", "index": 4 }, { - "pubkey": "0x35b47f3cb6239410ea02ed3d7af8c26822d32637383a0f64190722244ba7b54b412e016720db4d46d26d16656c7e402cda2ae557", + "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", "index": 5 }, { - "pubkey": "0x984c9749be3d147502b7961e7ff2422fbdfab85b9ddff5158d03b140f9ff371252475a0c54d1d1709f543b48dc475f0976958708", + "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", "index": 6 }, { - "pubkey": "0x730c97594267372a9512f76b600edc22e6ef65429f96d311d16264724ab0db1badc68f76390f54187c2557568bbe5e3cdc800364", + "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", "index": 7 }, { - "pubkey": "0x5ceb0c46c3e6c31d1408ee55915eaa37f85cb004c38ebe22b58b776d1883714f7a53a64c0fa51643e594ad3881b0c220459dbf68", + "pubkey": "0x21ade9268c60af157f924f637f4b9c3734b523121efa0b547e21c90d71c14340accca6326736800c2ef1bf61877c986d4ddf835e", "index": 8 } ] @@ -78,7 +79,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xbcd0edb081be11ca945afe6181970d2d17810310ef2fdb675f4c3eebacb8f461", + "stateRoot": "0x54b2feb4bf7af0733124b95734956ea836039d73c6729694f34d931d8153282e", "body": { "attestations": { "data": [] @@ -90,7 +91,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xd3addfb9676aa64ea20a1ef2bf6ceb8111a558cd87f8e9a34aafb545497ed57d", + "headRoot": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", "headRootLabel": "base" }, "stepType": "block", @@ -98,8 +99,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", - "stateRoot": "0x32b4da5abcf2eac66a62128260ab9cada56c858d134a3d97f6215102c928ef47", + "parentRoot": "0xf5f1658d4cf27f24ecb6e1eaba8e849f11bf21389c8356602f1fa1ef166bab30", + "stateRoot": "0xc38a9f8c6637d67b91e6afc7ee38c453e31d53489c8d40f82fb19f7f4af49665", "body": { "attestations": { "data": [] @@ -111,15 +112,15 @@ "data": { "slot": 1, "head": { - "root": "0xd3addfb9676aa64ea20a1ef2bf6ceb8111a558cd87f8e9a34aafb545497ed57d", + "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", "slot": 1 }, "target": { - "root": "0xd3addfb9676aa64ea20a1ef2bf6ceb8111a558cd87f8e9a34aafb545497ed57d", + "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", "slot": 1 }, "source": { - "root": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", + "root": "0xf5f1658d4cf27f24ecb6e1eaba8e849f11bf21389c8356602f1fa1ef166bab30", "slot": 0 } } @@ -131,7 +132,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x53b592f659917141535deff574ccebeebf8e557bca6a56859da38caed207134f", + "headRoot": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", "headRootLabel": "fork_a_1" }, "stepType": "block", @@ -139,8 +140,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xd3addfb9676aa64ea20a1ef2bf6ceb8111a558cd87f8e9a34aafb545497ed57d", - "stateRoot": "0xf23f8f1519f80bafaaabfdaef0bb3de6d67d632ef716104b91d4b4b44c0829a7", + "parentRoot": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", + "stateRoot": "0xcf9d5037d21452fe6face69ecb9399827ff5d7dde448c733530d9f013c30d59e", "body": { "attestations": { "data": [] @@ -152,15 +153,15 @@ "data": { "slot": 2, "head": { - "root": "0x53b592f659917141535deff574ccebeebf8e557bca6a56859da38caed207134f", + "root": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", "slot": 2 }, "target": { - "root": "0x53b592f659917141535deff574ccebeebf8e557bca6a56859da38caed207134f", + "root": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", "slot": 2 }, "source": { - "root": "0xd3addfb9676aa64ea20a1ef2bf6ceb8111a558cd87f8e9a34aafb545497ed57d", + "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", "slot": 1 } } @@ -172,7 +173,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x4eb89c8275c835c43939c787373c6ddfb8e4f9b3d130172af5debf72f1f0f787", + "headRoot": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -180,8 +181,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x53b592f659917141535deff574ccebeebf8e557bca6a56859da38caed207134f", - "stateRoot": "0x5a0472c71d734c2a0715372d019837954346f5a3d5e2bedffc36cf6b5e4eecb3", + "parentRoot": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", + "stateRoot": "0x0989b85535d514f2e157feb2721f285adf509a8ce7aaf47287e2307699b29bde", "body": { "attestations": { "data": [] @@ -193,15 +194,15 @@ "data": { "slot": 3, "head": { - "root": "0x4eb89c8275c835c43939c787373c6ddfb8e4f9b3d130172af5debf72f1f0f787", + "root": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", "slot": 3 }, "target": { - "root": "0x4eb89c8275c835c43939c787373c6ddfb8e4f9b3d130172af5debf72f1f0f787", + "root": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", "slot": 3 }, "source": { - "root": "0x53b592f659917141535deff574ccebeebf8e557bca6a56859da38caed207134f", + "root": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", "slot": 2 } } @@ -213,7 +214,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xd993adb078119a17b0eb95bbf7181d2bfa84d2b091ce3a4154d052ccfb39d962", + "headRoot": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -221,8 +222,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0x4eb89c8275c835c43939c787373c6ddfb8e4f9b3d130172af5debf72f1f0f787", - "stateRoot": "0xa3bd9a1dbf4e6cf84579ed95cd47c51f4e6838fa42fe9f89d4fa2e578743a432", + "parentRoot": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", + "stateRoot": "0xf3529b1b0adc1bb05ff6485e77b93abf1cc16c1c83e9c4932d24590fbedd8dff", "body": { "attestations": { "data": [] @@ -234,15 +235,15 @@ "data": { "slot": 4, "head": { - "root": "0xd993adb078119a17b0eb95bbf7181d2bfa84d2b091ce3a4154d052ccfb39d962", + "root": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", "slot": 4 }, "target": { - "root": "0xd993adb078119a17b0eb95bbf7181d2bfa84d2b091ce3a4154d052ccfb39d962", + "root": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", "slot": 4 }, "source": { - "root": "0x4eb89c8275c835c43939c787373c6ddfb8e4f9b3d130172af5debf72f1f0f787", + "root": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", "slot": 3 } } @@ -254,7 +255,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xd993adb078119a17b0eb95bbf7181d2bfa84d2b091ce3a4154d052ccfb39d962", + "headRoot": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -262,8 +263,8 @@ "block": { "slot": 5, "proposerIndex": 5, - "parentRoot": "0xd3addfb9676aa64ea20a1ef2bf6ceb8111a558cd87f8e9a34aafb545497ed57d", - "stateRoot": "0x85b7be514029e3f648f7a7387bf054f5847ab82d601f1a2ef501a535df821236", + "parentRoot": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", + "stateRoot": "0x6fa57208be09487b0cfaaabae48ccba9a335102fa55fd6ae2177d9e1b860bc32", "body": { "attestations": { "data": [] @@ -275,15 +276,15 @@ "data": { "slot": 5, "head": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", + "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", "slot": 5 }, "target": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", + "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", "slot": 5 }, "source": { - "root": "0xd3addfb9676aa64ea20a1ef2bf6ceb8111a558cd87f8e9a34aafb545497ed57d", + "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", "slot": 1 } } @@ -295,10 +296,10 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0x005b9cabe4f33578aa69d05ac4a49f5c5885af39cde4cc0cd7f6d1ae6dbfb2f2", + "headRoot": "0x192bc9efe1a001132499fad31956ae165c73d9b37219a7b1bc3b1f82a492412f", "headRootLabel": "fork_b_2", "latestJustifiedSlot": 5, - "latestJustifiedRoot": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", + "latestJustifiedRoot": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", "latestJustifiedRootLabel": "fork_b_1" }, "stepType": "block", @@ -306,115 +307,37 @@ "block": { "slot": 6, "proposerIndex": 6, - "parentRoot": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "stateRoot": "0x7ac39451f679bf43d9acb15fe1ff0dbabd7800e0428e1989177e9726a128ddf8", + "parentRoot": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", + "stateRoot": "0x14805a0ca35798cb8993b564dab4b7e725395224cf6e36fddf507b7f367c540d", "body": { "attestations": { "data": [ { - "validatorId": 0, + "aggregationBits": { + "data": [ + true, + true, + false, + false, + false, + true, + true, + true, + true + ] + }, "data": { "slot": 5, "head": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", + "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", "slot": 5 }, "target": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", + "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", "slot": 5 }, "source": { - "root": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", - "slot": 0 - } - } - }, - { - "validatorId": 1, - "data": { - "slot": 5, - "head": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "target": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "source": { - "root": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", - "slot": 0 - } - } - }, - { - "validatorId": 5, - "data": { - "slot": 5, - "head": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "target": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "source": { - "root": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", - "slot": 0 - } - } - }, - { - "validatorId": 6, - "data": { - "slot": 5, - "head": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "target": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "source": { - "root": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", - "slot": 0 - } - } - }, - { - "validatorId": 7, - "data": { - "slot": 5, - "head": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "target": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "source": { - "root": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", - "slot": 0 - } - } - }, - { - "validatorId": 8, - "data": { - "slot": 5, - "head": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "target": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", - "slot": 5 - }, - "source": { - "root": "0xc5001892dec217e47e8bc2ee224859fedce80d9a797002b1a654bd2730500b3a", + "root": "0xf5f1658d4cf27f24ecb6e1eaba8e849f11bf21389c8356602f1fa1ef166bab30", "slot": 0 } } @@ -428,15 +351,15 @@ "data": { "slot": 6, "head": { - "root": "0x005b9cabe4f33578aa69d05ac4a49f5c5885af39cde4cc0cd7f6d1ae6dbfb2f2", + "root": "0x192bc9efe1a001132499fad31956ae165c73d9b37219a7b1bc3b1f82a492412f", "slot": 6 }, "target": { - "root": "0x005b9cabe4f33578aa69d05ac4a49f5c5885af39cde4cc0cd7f6d1ae6dbfb2f2", + "root": "0x192bc9efe1a001132499fad31956ae165c73d9b37219a7b1bc3b1f82a492412f", "slot": 6 }, "source": { - "root": "0x6e1f24fcc04d276cb88c86264d82d27c95e507007a18d6314fadd4b754527c3c", + "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", "slot": 5 } } @@ -447,7 +370,7 @@ ], "maxSlot": 6, "_info": { - "hash": "0xc1716a403d3213e2a0eea3679c2bce49ceb96e0c2bd301cc7bfd1bc3745a9e7b", + "hash": "0xf9d5c81f2b9be058474fd5b825104a9ac5a0aa72884469b8005a57108a69fbfc", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_on_newly_justified_slot[fork_Devnet]", "description": "Reorg occurs correctly when forks cross justification boundaries.\n\n Scenario\n --------\n Two forks compete. Fork A is heavier and longer, but Fork B manages to\n become justified. Fork choice must switch to the justified fork regardless\n of weight/length.\n\n - Slot 1: Base\n - Slots 2-4: Fork A extends (becomes head with depth 3)\n - Slot 5: Fork B appears (descending from Base, skipping slots 2-4)\n - Slot 6: Fork B extends. This block contains enough attestations to\n justify Fork B at Slot 5.\n\n Expected Behavior\n -----------------\n 1. Fork A takes the lead initially (Slots 2-4) as the heaviest chain.\n 2. Fork B appears at Slot 5 but is initially lighter.\n 3. At Slot 6, the new block includes attestations that justify Fork B at Slot 5.\n 4. The justified checkpoint updates to Slot 5 (fork_b_1).\n 5. Fork A is immediately discarded because it does not descend from the new\n justified checkpoint (Fork A is on a branch from Slot 1).\n 6. Fork B becomes the canonical head.\n\n Why This Matters\n ----------------\n Justification is a critical safety mechanism:\n - Limits which blocks can be attested to\n - Ensures fork choice respects finality constraints\n\n This test ensures:\n - Reorgs respect justification boundaries\n - Fork choice works correctly across justifiable slots\n - Safety guarantees maintained during reorgs", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json similarity index 65% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json index c3114ec..48ba1b6 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_prevention_heavy_fork_resists_light_competition[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_prevention_heavy_fork_resists_light_competition[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,51 +31,51 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 }, { - "pubkey": "0xe9c00205c556b91d56cb8e5bd9427b3a3bd5b32aef243616c8af133b9a9b6f37c428ce3870fd3b231c960e26785cb3109e0fc115", + "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", "index": 4 }, { - "pubkey": "0x35b47f3cb6239410ea02ed3d7af8c26822d32637383a0f64190722244ba7b54b412e016720db4d46d26d16656c7e402cda2ae557", + "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", "index": 5 }, { - "pubkey": "0x984c9749be3d147502b7961e7ff2422fbdfab85b9ddff5158d03b140f9ff371252475a0c54d1d1709f543b48dc475f0976958708", + "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", "index": 6 }, { - "pubkey": "0x730c97594267372a9512f76b600edc22e6ef65429f96d311d16264724ab0db1badc68f76390f54187c2557568bbe5e3cdc800364", + "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", "index": 7 }, { - "pubkey": "0x5ceb0c46c3e6c31d1408ee55915eaa37f85cb004c38ebe22b58b776d1883714f7a53a64c0fa51643e594ad3881b0c220459dbf68", + "pubkey": "0x21ade9268c60af157f924f637f4b9c3734b523121efa0b547e21c90d71c14340accca6326736800c2ef1bf61877c986d4ddf835e", "index": 8 }, { - "pubkey": "0xe730a76d1315cf78c3e2da2bb0a04b4ed4368357b744605b3a356d252cea674fec3dab24cad98b10e6d8dd52cc67c54bf1dc853e", + "pubkey": "0xf977bd34c9b71d7d009bac5e61ba457349c1f83f1baeaf4884d35029792617306a92a2763ccacf223aa3d90c7d11321dd0634348", "index": 9 }, { - "pubkey": "0xe72aa07d461ab45ae96249796f554a4683f9f853ae7e2a759536fe70cac576216faa6d77d162797e53fc7c21c464696730874607", + "pubkey": "0xde697423036c96403425ed5cbdcee24eb3c5ef41d3de00798d3e683acf715f564b797b46b0de4a46513dfc4a8bc7ee2bd85c264b", "index": 10 }, { - "pubkey": "0x0a56662c41365d299d03405bad5fb6294c730d3c2e3d814f5df26b455e802567a762e4287db7b272130bc02d61de4c0e6b99297b", + "pubkey": "0x78b09c1f75c5f5761895d97db3e7a95f3add653b807a782d59bf154191ee6512d9189f1920bf9e55faa86d4aacd01e6c3766174e", "index": 11 } ] @@ -90,7 +91,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xc6c1567a1d685920cfee0fbcffe53a31d8419dbb072301e00850eac06e3e9064", + "stateRoot": "0x5f0cf3503e6923df8318f12208d86b9dacffb90b0d1e766c128e7e46eab7b11e", "body": { "attestations": { "data": [] @@ -102,7 +103,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xedb08c786703db131f6b2736e2c44e2d58ea2fce88a843bc69540ba185e749b3", + "headRoot": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", "headRootLabel": "base" }, "stepType": "block", @@ -110,8 +111,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xde20b818a4e5de71b44c93c2e228695f61b4ac048e8df9df3e8d6923f3780655", - "stateRoot": "0xbeb3734a870e084d51faf6de10b5ef82f4a06269d97dedd8cdb8ff1393a16ce6", + "parentRoot": "0xf7070586f3fd7653f33f8843e6cb6aad357fe7301cfbb686eb6e424ec86774dc", + "stateRoot": "0x79a635cee5d0035b7db166a69fa8c5ee548cbe90f130cdae1d690e6aa0240049", "body": { "attestations": { "data": [] @@ -123,15 +124,15 @@ "data": { "slot": 1, "head": { - "root": "0xedb08c786703db131f6b2736e2c44e2d58ea2fce88a843bc69540ba185e749b3", + "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", "slot": 1 }, "target": { - "root": "0xedb08c786703db131f6b2736e2c44e2d58ea2fce88a843bc69540ba185e749b3", + "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", "slot": 1 }, "source": { - "root": "0xde20b818a4e5de71b44c93c2e228695f61b4ac048e8df9df3e8d6923f3780655", + "root": "0xf7070586f3fd7653f33f8843e6cb6aad357fe7301cfbb686eb6e424ec86774dc", "slot": 0 } } @@ -143,7 +144,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb8a5de6fcb9524697c6b1c53627c291f308821299067369a4d3df8d877588933", + "headRoot": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -151,8 +152,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xedb08c786703db131f6b2736e2c44e2d58ea2fce88a843bc69540ba185e749b3", - "stateRoot": "0x31e962f2915d47e123476c84fdf346de175638e74fa496363e576db2bd54cd62", + "parentRoot": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", + "stateRoot": "0x73ae00ff75965ce1327db9a66989fc999b466d5b896b496188dd30f74c94426f", "body": { "attestations": { "data": [] @@ -164,15 +165,15 @@ "data": { "slot": 2, "head": { - "root": "0xb8a5de6fcb9524697c6b1c53627c291f308821299067369a4d3df8d877588933", + "root": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", "slot": 2 }, "target": { - "root": "0xb8a5de6fcb9524697c6b1c53627c291f308821299067369a4d3df8d877588933", + "root": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", "slot": 2 }, "source": { - "root": "0xedb08c786703db131f6b2736e2c44e2d58ea2fce88a843bc69540ba185e749b3", + "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", "slot": 1 } } @@ -184,7 +185,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x0160778abe942fc109f0513ad54c21b4f79dd6177ac658f9ba60d2e9b68da793", + "headRoot": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -192,8 +193,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb8a5de6fcb9524697c6b1c53627c291f308821299067369a4d3df8d877588933", - "stateRoot": "0x16dabd0cb302be2adb320ad2a9f6263131aad3f6f87de391fe78309a2f84a82f", + "parentRoot": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", + "stateRoot": "0x338fbd52756e4f705764c2e383b8ce9e3e0413533d4632bc4b431424f9ec989b", "body": { "attestations": { "data": [] @@ -205,15 +206,15 @@ "data": { "slot": 3, "head": { - "root": "0x0160778abe942fc109f0513ad54c21b4f79dd6177ac658f9ba60d2e9b68da793", + "root": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", "slot": 3 }, "target": { - "root": "0x0160778abe942fc109f0513ad54c21b4f79dd6177ac658f9ba60d2e9b68da793", + "root": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", "slot": 3 }, "source": { - "root": "0xb8a5de6fcb9524697c6b1c53627c291f308821299067369a4d3df8d877588933", + "root": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", "slot": 2 } } @@ -225,7 +226,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x9c43be6e9140c0f7c2b29fc4422e615cb206bf9212c0900d8005642f8db32a89", + "headRoot": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -233,8 +234,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0x0160778abe942fc109f0513ad54c21b4f79dd6177ac658f9ba60d2e9b68da793", - "stateRoot": "0x723a62759ca2def81f11686d78677760a1a21b861d5123650c938995c87a3c22", + "parentRoot": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", + "stateRoot": "0x42a4d61c5755f1dd4e10890d4736cf421d1e53265d19051e3c2331cf4fc394e3", "body": { "attestations": { "data": [] @@ -246,15 +247,15 @@ "data": { "slot": 4, "head": { - "root": "0x9c43be6e9140c0f7c2b29fc4422e615cb206bf9212c0900d8005642f8db32a89", + "root": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", "slot": 4 }, "target": { - "root": "0x9c43be6e9140c0f7c2b29fc4422e615cb206bf9212c0900d8005642f8db32a89", + "root": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", "slot": 4 }, "source": { - "root": "0x0160778abe942fc109f0513ad54c21b4f79dd6177ac658f9ba60d2e9b68da793", + "root": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", "slot": 3 } } @@ -266,7 +267,7 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0xef2796a18a603defc9dcc3dc642ad61f83f33dc5b7998f6ddbf022aaa46c7dc6", + "headRoot": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", "headRootLabel": "fork_a_5" }, "stepType": "block", @@ -274,8 +275,8 @@ "block": { "slot": 5, "proposerIndex": 5, - "parentRoot": "0x9c43be6e9140c0f7c2b29fc4422e615cb206bf9212c0900d8005642f8db32a89", - "stateRoot": "0x327deef04e5c018ab192c7d11f4f0f8565f951717abf17d6f239a63ba06659a1", + "parentRoot": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", + "stateRoot": "0x0d778725d02c44533ae9a136df18c0effe21ac44bbed86fdf817a203c3b00744", "body": { "attestations": { "data": [] @@ -287,15 +288,15 @@ "data": { "slot": 5, "head": { - "root": "0xef2796a18a603defc9dcc3dc642ad61f83f33dc5b7998f6ddbf022aaa46c7dc6", + "root": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", "slot": 5 }, "target": { - "root": "0xef2796a18a603defc9dcc3dc642ad61f83f33dc5b7998f6ddbf022aaa46c7dc6", + "root": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", "slot": 5 }, "source": { - "root": "0x9c43be6e9140c0f7c2b29fc4422e615cb206bf9212c0900d8005642f8db32a89", + "root": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", "slot": 4 } } @@ -307,7 +308,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xa7f3b0c85363e3b5eb8ebb186b73d4e7b24bcbe8b5745c0b05df2f7ddbc63369", + "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -315,8 +316,8 @@ "block": { "slot": 6, "proposerIndex": 6, - "parentRoot": "0xef2796a18a603defc9dcc3dc642ad61f83f33dc5b7998f6ddbf022aaa46c7dc6", - "stateRoot": "0xf179478bad67b5a1b70d6c714137bed1d71b19fd41b380c13c26da30928d0bc2", + "parentRoot": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", + "stateRoot": "0x53e4772b864743cff0f753902fdc0cf066c7ae0a0b7675d081a701930c2a71a8", "body": { "attestations": { "data": [] @@ -328,15 +329,15 @@ "data": { "slot": 6, "head": { - "root": "0xa7f3b0c85363e3b5eb8ebb186b73d4e7b24bcbe8b5745c0b05df2f7ddbc63369", + "root": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", "slot": 6 }, "target": { - "root": "0xa7f3b0c85363e3b5eb8ebb186b73d4e7b24bcbe8b5745c0b05df2f7ddbc63369", + "root": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", "slot": 6 }, "source": { - "root": "0xef2796a18a603defc9dcc3dc642ad61f83f33dc5b7998f6ddbf022aaa46c7dc6", + "root": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", "slot": 5 } } @@ -348,7 +349,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xa7f3b0c85363e3b5eb8ebb186b73d4e7b24bcbe8b5745c0b05df2f7ddbc63369", + "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -356,8 +357,8 @@ "block": { "slot": 7, "proposerIndex": 7, - "parentRoot": "0xedb08c786703db131f6b2736e2c44e2d58ea2fce88a843bc69540ba185e749b3", - "stateRoot": "0x3440f3c6654ec4d5e84b994cbf8efc8d9bd25579d2f09c85e6215d674d6171da", + "parentRoot": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", + "stateRoot": "0xcf40e9221cb344f06dd62fe12368941c5c9a853ad95446682a197463bb76fbae", "body": { "attestations": { "data": [] @@ -369,15 +370,15 @@ "data": { "slot": 7, "head": { - "root": "0xbfe1776828baf8a1ff30d4c3319614462b57751fc0f17982b440cbb463892893", + "root": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", "slot": 7 }, "target": { - "root": "0xbfe1776828baf8a1ff30d4c3319614462b57751fc0f17982b440cbb463892893", + "root": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", "slot": 7 }, "source": { - "root": "0xedb08c786703db131f6b2736e2c44e2d58ea2fce88a843bc69540ba185e749b3", + "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", "slot": 1 } } @@ -389,7 +390,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xa7f3b0c85363e3b5eb8ebb186b73d4e7b24bcbe8b5745c0b05df2f7ddbc63369", + "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -397,8 +398,8 @@ "block": { "slot": 8, "proposerIndex": 8, - "parentRoot": "0xbfe1776828baf8a1ff30d4c3319614462b57751fc0f17982b440cbb463892893", - "stateRoot": "0x1513a31d4d48f226d823027e90a00b62a77151f42f42885e0c0fb50461812642", + "parentRoot": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", + "stateRoot": "0x31df55327a5165ed894e0e6750494fe93db62b7151b8a36dfe0681f8cf187577", "body": { "attestations": { "data": [] @@ -410,15 +411,15 @@ "data": { "slot": 8, "head": { - "root": "0x31d910b865b336ca684e51378ea2673bc8d2347b2b1182c447a69c2b5e685ace", + "root": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", "slot": 8 }, "target": { - "root": "0x31d910b865b336ca684e51378ea2673bc8d2347b2b1182c447a69c2b5e685ace", + "root": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", "slot": 8 }, "source": { - "root": "0xbfe1776828baf8a1ff30d4c3319614462b57751fc0f17982b440cbb463892893", + "root": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", "slot": 7 } } @@ -430,7 +431,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xa7f3b0c85363e3b5eb8ebb186b73d4e7b24bcbe8b5745c0b05df2f7ddbc63369", + "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -438,8 +439,8 @@ "block": { "slot": 9, "proposerIndex": 9, - "parentRoot": "0x31d910b865b336ca684e51378ea2673bc8d2347b2b1182c447a69c2b5e685ace", - "stateRoot": "0x5fafb4a9e8fe8149d60baf40ca4a3ed7a804e6e2b6f4882987fbb749afc07010", + "parentRoot": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", + "stateRoot": "0x4e670fcc919834884e77494cf2668a36ce595e0c83101a59c4b9913560f72e39", "body": { "attestations": { "data": [] @@ -451,15 +452,15 @@ "data": { "slot": 9, "head": { - "root": "0xd3e8ca8ecdd02327fde51e1d4535b17ca1955fd2d2468ecda0a88954d904edca", + "root": "0x1ba5d7d59a3de96f9c89215bd8a886f080876e8d711759f67102ad8988790764", "slot": 9 }, "target": { - "root": "0xd3e8ca8ecdd02327fde51e1d4535b17ca1955fd2d2468ecda0a88954d904edca", + "root": "0x1ba5d7d59a3de96f9c89215bd8a886f080876e8d711759f67102ad8988790764", "slot": 9 }, "source": { - "root": "0x31d910b865b336ca684e51378ea2673bc8d2347b2b1182c447a69c2b5e685ace", + "root": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", "slot": 8 } } @@ -470,7 +471,7 @@ ], "maxSlot": 9, "_info": { - "hash": "0xc456239e8fc06ecb6ef3f0af7026a523ffb744c87d9c6ba3e5ce97c5538d4c0e", + "hash": "0x094dbb98a34784ad9fcdeef562f939966d88c5d821f09dff1eb11710756a9199", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_prevention_heavy_fork_resists_light_competition[fork_Devnet]", "description": "Established heavy fork successfully resists light competing fork.\n\n Scenario\n --------\n - Fork A builds substantial lead (5 blocks)\n - Fork B created late, builds 3 blocks\n - Fork A maintains head despite fork B's growth\n\n Chain Evolution:\n Slots 1-5: Fork A builds uncontested (5 blocks)\n Slot 6: Fork B starts from slot 1 (late competitor)\n Slots 6-8: Fork B builds 3 blocks (total 3 vs fork A's 5)\n Result: Fork A remains canonical (reorg prevented)\n\n Expected Behavior\n -----------------\n 1. Fork A establishes 5-block lead\n 2. Fork B starts competing from an earlier slot\n 3. Fork B builds rapidly but can't match fork A's depth\n 4. Head remains on fork A throughout (no reorg)\n\n Why This Matters\n ----------------\n Reorg resistance is crucial for chain stability:\n - Prevents cheap disruption of established chain\n - Requires substantial work to overtake canonical fork\n - Protects against late-arriving competing forks\n - Ensures finality can eventually be reached\n\n Attack Prevention:\n - Attacker can't easily reorg established blocks\n - Must match or exceed weight of canonical chain\n - Time advantage gives canonical chain strong position\n - Network naturally converges on heaviest fork", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json similarity index 67% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json index b4bf654..6877c19 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_with_slot_gaps[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_with_slot_gaps[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,43 +31,43 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 }, { - "pubkey": "0xe9c00205c556b91d56cb8e5bd9427b3a3bd5b32aef243616c8af133b9a9b6f37c428ce3870fd3b231c960e26785cb3109e0fc115", + "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", "index": 4 }, { - "pubkey": "0x35b47f3cb6239410ea02ed3d7af8c26822d32637383a0f64190722244ba7b54b412e016720db4d46d26d16656c7e402cda2ae557", + "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", "index": 5 }, { - "pubkey": "0x984c9749be3d147502b7961e7ff2422fbdfab85b9ddff5158d03b140f9ff371252475a0c54d1d1709f543b48dc475f0976958708", + "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", "index": 6 }, { - "pubkey": "0x730c97594267372a9512f76b600edc22e6ef65429f96d311d16264724ab0db1badc68f76390f54187c2557568bbe5e3cdc800364", + "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", "index": 7 }, { - "pubkey": "0x5ceb0c46c3e6c31d1408ee55915eaa37f85cb004c38ebe22b58b776d1883714f7a53a64c0fa51643e594ad3881b0c220459dbf68", + "pubkey": "0x21ade9268c60af157f924f637f4b9c3734b523121efa0b547e21c90d71c14340accca6326736800c2ef1bf61877c986d4ddf835e", "index": 8 }, { - "pubkey": "0xe730a76d1315cf78c3e2da2bb0a04b4ed4368357b744605b3a356d252cea674fec3dab24cad98b10e6d8dd52cc67c54bf1dc853e", + "pubkey": "0xf977bd34c9b71d7d009bac5e61ba457349c1f83f1baeaf4884d35029792617306a92a2763ccacf223aa3d90c7d11321dd0634348", "index": 9 } ] @@ -82,7 +83,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0x9bfe540b7ec58ff9a78081743314622119e25b163a81944afa157a48de9141a9", + "stateRoot": "0xdd4d50777a2795c87692b1439bbdb6040b261083b5a3fdb227842143320d66a4", "body": { "attestations": { "data": [] @@ -94,7 +95,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xebd4d2414092fded77b5296c5bec63d24613f0ea3e9ab09093edc894123c7e7b", + "headRoot": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", "headRootLabel": "base" }, "stepType": "block", @@ -102,8 +103,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0x630694e9dab0cf81bb46b6afb8afefef0dfd31727e2288d10e6bea0dcf89d7c5", - "stateRoot": "0xf44f16e98828e0444986db31f8dc7e3c6da03eccc3939a612fb625c2e6da1c4f", + "parentRoot": "0x6a2326da9e435e296e0f05d37b574c49de52ba9c004c1d02a83418e092bda242", + "stateRoot": "0x78e57b0b1b061035b18449818a6a79866a9f2b81fe310c272160e39724cec1e1", "body": { "attestations": { "data": [] @@ -115,15 +116,15 @@ "data": { "slot": 1, "head": { - "root": "0xebd4d2414092fded77b5296c5bec63d24613f0ea3e9ab09093edc894123c7e7b", + "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", "slot": 1 }, "target": { - "root": "0xebd4d2414092fded77b5296c5bec63d24613f0ea3e9ab09093edc894123c7e7b", + "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", "slot": 1 }, "source": { - "root": "0x630694e9dab0cf81bb46b6afb8afefef0dfd31727e2288d10e6bea0dcf89d7c5", + "root": "0x6a2326da9e435e296e0f05d37b574c49de52ba9c004c1d02a83418e092bda242", "slot": 0 } } @@ -135,7 +136,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x2931509766968cc5af24ebb5e90220c5127bb914e7206bdff7c7b16c19e84769", + "headRoot": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -143,8 +144,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xebd4d2414092fded77b5296c5bec63d24613f0ea3e9ab09093edc894123c7e7b", - "stateRoot": "0x3d203300e0d4bb8ff5cafa7539e3fce4bc875941a8a4d51ccc20ade3bd34bc04", + "parentRoot": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", + "stateRoot": "0x498d7fb7b00193a59401b781fd8cc2efb0e9892cff5b4025e92da1bdef6a2788", "body": { "attestations": { "data": [] @@ -156,15 +157,15 @@ "data": { "slot": 3, "head": { - "root": "0x2931509766968cc5af24ebb5e90220c5127bb914e7206bdff7c7b16c19e84769", + "root": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", "slot": 3 }, "target": { - "root": "0x2931509766968cc5af24ebb5e90220c5127bb914e7206bdff7c7b16c19e84769", + "root": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", "slot": 3 }, "source": { - "root": "0xebd4d2414092fded77b5296c5bec63d24613f0ea3e9ab09093edc894123c7e7b", + "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", "slot": 1 } } @@ -176,7 +177,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x2931509766968cc5af24ebb5e90220c5127bb914e7206bdff7c7b16c19e84769", + "headRoot": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -184,8 +185,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0xebd4d2414092fded77b5296c5bec63d24613f0ea3e9ab09093edc894123c7e7b", - "stateRoot": "0x2acbc678ea50c3833e0b575b8ef9229cc76b962ac376e6a4ac6ad9b22692b56e", + "parentRoot": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", + "stateRoot": "0x22d4e916d14d96579adc6c729933ea817c7be0686d611f88e5f3b2ac7a2b49e7", "body": { "attestations": { "data": [] @@ -197,15 +198,15 @@ "data": { "slot": 4, "head": { - "root": "0x7ab8e2219f52e0771efb0f2f33a61eaf9ca7b1c5552d4091f8430ed2099d064b", + "root": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", "slot": 4 }, "target": { - "root": "0x7ab8e2219f52e0771efb0f2f33a61eaf9ca7b1c5552d4091f8430ed2099d064b", + "root": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", "slot": 4 }, "source": { - "root": "0xebd4d2414092fded77b5296c5bec63d24613f0ea3e9ab09093edc894123c7e7b", + "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", "slot": 1 } } @@ -220,8 +221,8 @@ "block": { "slot": 7, "proposerIndex": 7, - "parentRoot": "0x2931509766968cc5af24ebb5e90220c5127bb914e7206bdff7c7b16c19e84769", - "stateRoot": "0x92e6191597273ca9a91ecd8b77294eeb4e8ad539af58f414abfd9d15ead6b15e", + "parentRoot": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", + "stateRoot": "0xb082c9d166e932e34355c7b563062f844d8b3dcd595804210600d19cf720d4df", "body": { "attestations": { "data": [] @@ -233,15 +234,15 @@ "data": { "slot": 7, "head": { - "root": "0xe626e63401f32d6b1f315d7f4093ed8d0cf8d5ad9f157415f59f5ec6f5a2faed", + "root": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", "slot": 7 }, "target": { - "root": "0xe626e63401f32d6b1f315d7f4093ed8d0cf8d5ad9f157415f59f5ec6f5a2faed", + "root": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", "slot": 7 }, "source": { - "root": "0x2931509766968cc5af24ebb5e90220c5127bb914e7206bdff7c7b16c19e84769", + "root": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", "slot": 3 } } @@ -253,7 +254,7 @@ "valid": true, "checks": { "headSlot": 7, - "headRoot": "0xe626e63401f32d6b1f315d7f4093ed8d0cf8d5ad9f157415f59f5ec6f5a2faed", + "headRoot": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", "headRootLabel": "fork_a_7" }, "stepType": "tick", @@ -263,7 +264,7 @@ "valid": true, "checks": { "headSlot": 7, - "headRoot": "0xe626e63401f32d6b1f315d7f4093ed8d0cf8d5ad9f157415f59f5ec6f5a2faed", + "headRoot": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", "headRootLabel": "fork_a_7" }, "stepType": "block", @@ -271,8 +272,8 @@ "block": { "slot": 8, "proposerIndex": 8, - "parentRoot": "0x7ab8e2219f52e0771efb0f2f33a61eaf9ca7b1c5552d4091f8430ed2099d064b", - "stateRoot": "0xff027ad45e22935934cb87dda68fc5132dea16c9fc202b916048e0e9b6d18b78", + "parentRoot": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", + "stateRoot": "0xe5799cce3053ed18101f058b86279ee3506cdc043dfd3d9459e7c8cd275dd70b", "body": { "attestations": { "data": [] @@ -284,15 +285,15 @@ "data": { "slot": 8, "head": { - "root": "0xee8ea3f3dc777e5b64abecbe5727aa3091f1ecc895811cafe0c96b7307785a8b", + "root": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", "slot": 8 }, "target": { - "root": "0xee8ea3f3dc777e5b64abecbe5727aa3091f1ecc895811cafe0c96b7307785a8b", + "root": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", "slot": 8 }, "source": { - "root": "0x7ab8e2219f52e0771efb0f2f33a61eaf9ca7b1c5552d4091f8430ed2099d064b", + "root": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", "slot": 4 } } @@ -307,8 +308,8 @@ "block": { "slot": 9, "proposerIndex": 9, - "parentRoot": "0xee8ea3f3dc777e5b64abecbe5727aa3091f1ecc895811cafe0c96b7307785a8b", - "stateRoot": "0x2e475a8023418a8fd29193de900b10f0adfc3a59a5fdc33947fa351a1705d096", + "parentRoot": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", + "stateRoot": "0x6606c45e80ca56d447cc01fb1d2883faaa377f15fb3345a14d4ffe1c10065d66", "body": { "attestations": { "data": [] @@ -320,15 +321,15 @@ "data": { "slot": 9, "head": { - "root": "0xdcb11b44abfb34b2aa9ff47af18c5fd9b4520d27dad895882824085178501e08", + "root": "0x57a0c119d58646e11a0c8b60efb893537011abb8e026282d95f78bb08b6ee5de", "slot": 9 }, "target": { - "root": "0xdcb11b44abfb34b2aa9ff47af18c5fd9b4520d27dad895882824085178501e08", + "root": "0x57a0c119d58646e11a0c8b60efb893537011abb8e026282d95f78bb08b6ee5de", "slot": 9 }, "source": { - "root": "0xee8ea3f3dc777e5b64abecbe5727aa3091f1ecc895811cafe0c96b7307785a8b", + "root": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", "slot": 8 } } @@ -340,7 +341,7 @@ "valid": true, "checks": { "headSlot": 9, - "headRoot": "0xdcb11b44abfb34b2aa9ff47af18c5fd9b4520d27dad895882824085178501e08", + "headRoot": "0x57a0c119d58646e11a0c8b60efb893537011abb8e026282d95f78bb08b6ee5de", "headRootLabel": "fork_b_9" }, "stepType": "tick", @@ -349,7 +350,7 @@ ], "maxSlot": 9, "_info": { - "hash": "0xafa812bf81e174ef5e55cbba0d26fa6e3def306af6acf14c796173ef6ec8928d", + "hash": "0xbe1f146cff42919afac43d40b9c9d411db32527e3937cfdc31bf763cd8abf651", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_with_slot_gaps[fork_Devnet]", "description": "Reorg occurs correctly even with missed slots in the chain.\n\n Scenario\n --------\n - Slot 1: Base\n - Slot 3: Fork A (skipping slot 2)\n - Slot 4: Fork B (competing)\n - Slot 7: Fork A extended (skipping slots 4-6)\n - Slot 8: Fork B extended (skipping slots 5-7)\n - Slot 9: Fork B extended again \u2192 triggers reorg\n\n Missed Slots: 2, 5, 6 (no blocks produced)\n\n Expected Behavior\n -----------------\n 1. Sparse block production doesn't affect fork choice logic\n 2. Weight calculation only considers actual blocks\n 3. Reorg happens based on block count, not slot numbers\n 4. Fork B with 3 blocks beats fork A with 2 blocks\n\n Reorg Details:\n - **Depth**: 2 blocks (fork_a slots 3, 7)\n - **Trigger**: Progressive building despite gaps\n - **Weight**: 3 proposer attestations vs 2\n\n Why This Matters\n ----------------\n Missed slots are extremely common in production:\n - Offline validators (expected ~1% downtime)\n - Network issues preventing timely block propagation\n - Intentional skips during network congestion\n\n Fork choice must remain robust with sparse block production:\n - Gaps don't create bias toward any fork\n - Only actual blocks contribute weight\n - Reorg logic works identically whether slots are consecutive or sparse\n\n This test ensures the algorithm works correctly in realistic network\n conditions where perfect block production is impossible.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_simple_one_block_reorg.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json similarity index 70% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_simple_one_block_reorg.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json index 57ecea6..2fd5863 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_simple_one_block_reorg.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_simple_one_block_reorg[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_simple_one_block_reorg[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -70,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "headRootLabel": "chain_base" }, "stepType": "block", @@ -78,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -91,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -111,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -119,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -132,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -160,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -173,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -193,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -201,8 +202,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -214,15 +215,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -233,7 +234,7 @@ ], "maxSlot": 3, "_info": { - "hash": "0xd04c1b121a4d52cbe5a471f6f44d6660cb3bd79b3140627992e94acfec79e03d", + "hash": "0x9e630325338a717aedd549d4636b040fb8cb5fbd63f84024c6c77d6379b1acf0", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_simple_one_block_reorg[fork_Devnet]", "description": "Simplest reorg: one-block fork overtakes another via extension.\n\n Scenario\n --------\n - Slot 1: Common ancestor (chain_base)\n - Slot 2: Fork A created, becomes head\n - Slot 2: Fork B created (competing fork at same slot)\n - Slot 3: Fork B extended \u2192 triggers reorg from A to B\n\n Expected Behavior\n -----------------\n 1. After fork_a_2: head = fork_a_2 (first fork created)\n 2. After fork_b_2: head = fork_a_2 (equal weight, head remains unchanged)\n 3. After fork_b_3: head = fork_b_3 (fork B heavier due to extension)\n\n Reorg Details:\n - **Depth**: 1 block (fork_a_2 becomes non-canonical)\n - **Trigger**: Fork extension (proposer attestation)\n - **Weight advantage**: Fork B has 2 proposer attestations vs 1\n\n Why This Matters\n ----------------\n This is the most common reorg scenario in practice:\n - Two blocks proposed at nearly the same time\n - Network temporarily splits (half see A first, half see B first)\n - Next proposer builds on one fork, resolving the split\n - Fork choice converges to the extended fork\n\n Tests the fundamental property: extending a fork makes it heavier.", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_three_block_deep_reorg.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json similarity index 62% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_three_block_deep_reorg.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json index d658ad4..c7e77c3 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_three_block_deep_reorg.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_block_deep_reorg[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_block_deep_reorg[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,20 +31,28 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 + }, + { + "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "index": 4 + }, + { + "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "index": 5 } ] }, @@ -58,7 +67,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe483b5237726c89bec17cd6aa4dd397be5b4952d73e7e79d4c4c40b88734227e", "body": { "attestations": { "data": [] @@ -70,7 +79,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "headRootLabel": "base" }, "stepType": "block", @@ -78,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", + "stateRoot": "0x6f64b6379a51d5ea3a51eee1dde0e2457e89942177a60a0705ad295acadf7674", "body": { "attestations": { "data": [] @@ -91,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", "slot": 0 } } @@ -111,7 +120,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -119,8 +128,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", "body": { "attestations": { "data": [] @@ -132,15 +141,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 } } @@ -152,7 +161,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -160,8 +169,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", "body": { "attestations": { "data": [] @@ -173,15 +182,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", "slot": 1 } } @@ -193,7 +202,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -201,8 +210,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", "body": { "attestations": { "data": [] @@ -214,15 +223,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 } } @@ -234,7 +243,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -242,8 +251,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", "body": { "attestations": { "data": [] @@ -255,15 +264,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", "slot": 2 } } @@ -275,16 +284,16 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "headRootLabel": "fork_a_4" }, "stepType": "block", "block": { "block": { "slot": 4, - "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "proposerIndex": 4, + "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", "body": { "attestations": { "data": [] @@ -292,19 +301,19 @@ } }, "proposerAttestation": { - "validatorId": 0, + "validatorId": 4, "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 } } @@ -316,16 +325,16 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "headRootLabel": "fork_a_4" }, "stepType": "block", "block": { "block": { "slot": 4, - "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "proposerIndex": 4, + "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", "body": { "attestations": { "data": [] @@ -333,19 +342,19 @@ } }, "proposerAttestation": { - "validatorId": 0, + "validatorId": 4, "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", "slot": 3 } } @@ -357,16 +366,16 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "headRoot": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", "headRootLabel": "fork_b_5" }, "stepType": "block", "block": { "block": { "slot": 5, - "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "proposerIndex": 5, + "parentRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "stateRoot": "0xa2b05e5ab5824c023fcfce5f1e9276d95c2c034ed07721d059d5ad2c07007d99", "body": { "attestations": { "data": [] @@ -374,19 +383,19 @@ } }, "proposerAttestation": { - "validatorId": 1, + "validatorId": 5, "data": { "slot": 5, "head": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", "slot": 5 }, "target": { - "root": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", + "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", "slot": 5 }, "source": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", "slot": 4 } } @@ -397,10 +406,10 @@ ], "maxSlot": 5, "_info": { - "hash": "0x538ba09d1401eae371c759bea65ad01386f228196396a6427205fd80c911501a", + "hash": "0x386b07f20dea1eb1a6a769cf196cfe4880e3b2c03d49bb7df82a8bb32adc4e41", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_block_deep_reorg[fork_Devnet]", - "description": "Deep three-block reorg from established fork to alternative.\n\n Scenario\n --------\n - Slot 1: Common base\n - Slots 2-4: Fork A builds 3-block lead\n - Slots 2-5: Fork B slowly builds, then surpasses with 4 blocks\n\n Timeline:\n Slot 2: Fork A leads (1 vs 0)\n Slot 3: Fork A leads (2 vs 1)\n Slot 4: Fork A leads (3 vs 2)\n Slot 5: Fork B overtakes (4 vs 3) \u2192 3-block deep reorg\n\n Expected Behavior\n -----------------\n 1. Fork A establishes 3-block canonical chain (slots 2-4)\n 2. Fork B steadily builds parallel chain\n 3. At slot 5, fork B has 4 blocks vs fork A's 3 blocks\n 4. Fork choice switches to fork B\n 5. Three blocks (fork_a slots 2-4) become non-canonical\n\n Reorg Details:\n - **Depth**: 3 blocks (deepest in this test suite)\n - **Trigger**: Alternative fork becomes longer\n - **Weight advantage**: 4 proposer attestations vs 3\n\n Why This Matters\n ----------------\n Deep reorgs (3+ blocks) are rare in healthy networks but can happen:\n - Network partitions lasting multiple slots\n - Coordinated validator behavior (intentional or accidental)\n - Major network latency events\n\n Properties verified:\n - Fork choice correctly switches even after multiple canonical blocks\n - Weight calculation works correctly over extended depth\n - No \"stickiness\" bias toward existing head\n - Objective heaviest fork always wins\n\n This tests the protocol's ability to recover from significant disagreement\n about chain history, ensuring safety and liveness even in adversarial scenarios.", + "description": "Deep three-block reorg from established fork to alternative.\n\n Scenario\n --------\n - Slot 1: Common base\n - Slots 2-4: Fork A builds 3-block lead\n - Slots 2-5: Fork B slowly builds, then surpasses with 4 blocks\n\n Timeline:\n Slot 2: Fork A leads (1 vs 0)\n Slot 3: Fork A leads (2 vs 1)\n Slot 4: Fork A leads (3 vs 2)\n Slot 5: Fork B overtakes (4 vs 3) \u2192 3-block deep reorg\n\n Expected Behavior\n -----------------\n 1. Fork A establishes 3-block canonical chain (slots 2-4)\n 2. Fork B steadily builds parallel chain\n 3. At slot 5, fork B has 4 blocks vs fork A's 3 blocks\n 4. Fork choice switches to fork B\n 5. Three blocks (fork_a slots 2-4) become non-canonical\n\n Reorg Details:\n - **Depth**: 3 blocks (deepest in this test suite)\n - **Trigger**: Alternative fork becomes longer\n\n Why This Matters\n ----------------\n Deep reorgs (3+ blocks) are rare in healthy networks but can happen:\n - Network partitions lasting multiple slots\n - Coordinated validator behavior (intentional or accidental)\n - Major network latency events\n\n Properties verified:\n - Fork choice correctly switches even after multiple canonical blocks\n - Weight calculation works correctly over extended depth\n - No \"stickiness\" bias toward existing head\n - Objective heaviest fork always wins\n\n This tests the protocol's ability to recover from significant disagreement\n about chain history, ensuring safety and liveness even in adversarial scenarios.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_three_way_fork_competition.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json similarity index 69% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_three_way_fork_competition.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json index cb87cfc..b52c7f3 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_three_way_fork_competition.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_way_fork_competition[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_way_fork_competition[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -70,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "headRootLabel": "base" }, "stepType": "block", @@ -78,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -91,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -111,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -119,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -132,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -160,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -173,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -193,7 +194,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -201,8 +202,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -214,15 +215,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -234,7 +235,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_c_3" }, "stepType": "block", @@ -242,8 +243,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -255,15 +256,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -275,7 +276,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_c_3" }, "stepType": "block", @@ -283,8 +284,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -296,15 +297,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -316,7 +317,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "headRootLabel": "fork_b_4" }, "stepType": "block", @@ -324,8 +325,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -337,15 +338,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -356,7 +357,7 @@ ], "maxSlot": 4, "_info": { - "hash": "0x6abe25bb6e34bc5c661bcc1bb78fd4c82776340c9e46f5ddb62dc11add58c277", + "hash": "0x22a626a9971fbedce04e2cb93c8dc0bd769664a284741c1898da84d16a3a3bb6", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_way_fork_competition[fork_Devnet]", "description": "Three competing forks with progressive elimination until one wins.\n\n Scenario\n --------\n Three forks (A, B, C) compete simultaneously. Fork choice progressively\n eliminates weaker forks as stronger ones extend.\n\n Fork Topology:\n base (slot 1)\n / | / | fork_a fork_b fork_c (slot 2)\n | | |\n | | +--- fork_c_3 (slot 3)\n | +--- fork_b_3 (slot 3)\n | +--- fork_b_4 (slot 4) \u2190 Winner\n +--- abandoned\n\n Expected Behavior\n -----------------\n 1. All three forks start at slot 2 (three-way tie)\n 2. Fork C extends to slot 3 \u2192 becomes head\n 3. Fork B extends to slot 3 \u2192 ties with fork C at depth 2\n 4. Fork B extends to slot 4 \u2192 wins with depth 3\n 5. Forks A and C become non-canonical\n\n Reorg Sequence:\n - Initial: fork_a (tie-breaker among three)\n - After fork_c_3: fork_c (depth advantage)\n - After fork_b_3: fork_c (tie, maintains head)\n - After fork_b_4: fork_b (final winner)\n\n Why This Matters\n ----------------\n Multi-fork scenarios can occur during:\n - Network partitions splitting validators 3+ ways\n - Rapid block production creating multiple conflicting proposals\n - Byzantine validators intentionally creating competing forks\n\n Properties verified:\n - Fork choice handles 3+ simultaneous competing forks\n - Head selection remains consistent and deterministic\n - Progressive elimination works correctly\n - Final winner is objectively the heaviest fork", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json similarity index 69% rename from lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json index 1e14bf1..b7bf9f8 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_two_block_reorg_progressive_building[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_two_block_reorg_progressive_building[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -70,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "headRootLabel": "base" }, "stepType": "block", @@ -78,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -91,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -111,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -119,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -132,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -160,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -173,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -193,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -201,8 +202,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -214,15 +215,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -234,7 +235,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -242,8 +243,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -255,15 +256,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -275,7 +276,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "headRootLabel": "fork_b_4" }, "stepType": "block", @@ -283,8 +284,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -296,15 +297,15 @@ "data": { "slot": 4, "head": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "target": { - "root": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", + "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", "slot": 4 }, "source": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 } } @@ -315,7 +316,7 @@ ], "maxSlot": 4, "_info": { - "hash": "0x2e3a46ed9f279e85a86c36750104362ddbde64bc409b9e918a454dc389a4de1e", + "hash": "0x761e172b9090081b7b34254bbd54eb3e51338de2f9b80e5f8d28fd5a1b673eb8", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_two_block_reorg_progressive_building[fork_Devnet]", "description": "Two-block reorg via progressive fork building.\n\n Scenario\n --------\n - Slot 1: Common ancestor\n - Slots 2-3: Fork A extends to 2 blocks ahead\n - Slots 2-4: Fork B slowly catches up, then overtakes\n\n Chain State Evolution:\n Slot 1: base\n Slot 2: base \u2190 fork_a_2 (head)\n base \u2190 fork_b_2\n Slot 3: base \u2190 fork_a_2 \u2190 fork_a_3 (head)\n base \u2190 fork_b_2\n Slot 4: base \u2190 fork_a_2 \u2190 fork_a_3 (was head)\n base \u2190 fork_b_2 \u2190 fork_b_3 (tie at depth 2)\n Slot 5: base \u2190 fork_a_2 \u2190 fork_a_3 (abandoned)\n base \u2190 fork_b_2 \u2190 fork_b_3 \u2190 fork_b_4 (head - REORG!)\n\n Expected Behavior\n -----------------\n 1. Fork A leads for slots 2-3 (2 blocks ahead)\n 2. Fork B catches up at slot 4 (both at depth 2)\n 3. Fork B overtakes at slot 5 (3 blocks vs 2)\n 4. Two-block reorg: fork_a_2 and fork_a_3 become non-canonical\n\n Reorg Details:\n - **Depth**: 2 blocks\n - **Trigger**: Progressive building on alternative fork\n - **Weight advantage**: Fork B has 3 proposer attestations vs 2\n\n Why This Matters\n ----------------\n Demonstrates that an initially leading fork can be overtaken if:\n - Proposers switch to building on the alternative fork\n - The alternative fork accumulates more blocks over time\n - Network temporarily favored one fork but consensus shifted", diff --git a/lean_client/tests/test_vectors/test_fork_choice/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json similarity index 69% rename from lean_client/tests/test_vectors/test_fork_choice/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json rename to lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json index 3038e80..bc8289c 100644 --- a/lean_client/tests/test_vectors/test_fork_choice/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/fc/test_lexicographic_tiebreaker.py::test_equal_weight_forks_use_lexicographic_tiebreaker[fork_Devnet][fork_Devnet-fork_choice_test]": { + "tests/consensus/devnet/fc/test_lexicographic_tiebreaker.py::test_equal_weight_forks_use_lexicographic_tiebreaker[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xceb0487e744334f030a8310f38b89fc8fc6df7adc95d6eca6ead447bd59bf07c", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", "body": { "attestations": { "data": [] @@ -70,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "headRootLabel": "base" }, "stepType": "block", @@ -78,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -91,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "target": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 }, "source": { - "root": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "slot": 0 } } @@ -111,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -119,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -132,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -152,7 +153,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -160,8 +161,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -173,15 +174,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -193,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -201,8 +202,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -214,15 +215,15 @@ "data": { "slot": 2, "head": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "target": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 }, "source": { - "root": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", + "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", "slot": 1 } } @@ -244,8 +245,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -257,15 +258,15 @@ "data": { "slot": 3, "head": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "target": { - "root": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", + "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", "slot": 3 }, "source": { - "root": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", + "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", "slot": 2 } } @@ -276,7 +277,7 @@ ], "maxSlot": 3, "_info": { - "hash": "0xb8f377ced6262992cbe5001cde8b8a05c5fd263ee07fc151d81817832b5a012c", + "hash": "0x268fc7be347b5cf7f041b3136edcd3ca1348ac4ff4606923c8e939c75d63c08c", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_lexicographic_tiebreaker.py::test_equal_weight_forks_use_lexicographic_tiebreaker[fork_Devnet]", "description": "Fork choice selects lexicographically highest branch when fork weights tie.\n\n Scenario\n --------\n - Slot 1: Build common ancestor\n - Slots 2-3: Build fork A to depth 2 (slots 2 & 3)\n - Slots 2-3: Build fork B to depth 2 (slots 2 & 3)\n\n Both forks have identical structure:\n - Same depth (2 blocks each)\n - Same attestation weight (2 proposer attestations each)\n - Same parent (common ancestor at slot 1)\n\n Expected Behavior\n -----------------\n The competing forks have identical attestation weight. The head is chosen\n via lexicographic ordering of the block roots. The framework automatically\n verifies that:\n 1. Both forks are at the same slot (equal depth)\n 2. The head is the lexicographically highest root among them", From 88bd9b236cea5a1b3d950644717deed24b59bb9d Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:23:16 +0200 Subject: [PATCH 12/48] enable verify signatures/state transition tests --- lean_client/Cargo.lock | 1 + lean_client/Cargo.toml | 1 + lean_client/Makefile | 2 +- lean_client/containers/Cargo.toml | 1 + .../containers/tests/debug_deserialize.rs | 68 - lean_client/containers/tests/main.rs | 1 - .../tests/test_vectors/block_processing.rs | 95 +- .../containers/tests/test_vectors/genesis.rs | 27 +- .../tests/test_vectors/verify_signatures.rs | 65 +- lean_client/fork_choice/Cargo.toml | 2 +- .../test_block_at_large_slot_number.json | 17 +- .../test_block_extends_deep_chain.json | 93 +- .../test_block_with_invalid_parent_root.json | 13 +- .../test_block_with_invalid_proposer.json | 15 +- .../test_block_with_invalid_state_root.json | 15 +- .../test_block_with_wrong_slot.json | 81 + .../test_blocks_with_gaps.json | 25 +- .../test_empty_blocks.json | 37 +- .../test_empty_blocks_with_missed_slots.json | 33 +- .../test_linear_chain_multiple_blocks.json | 33 +- ...est_process_first_block_after_genesis.json | 17 +- .../test_genesis_custom_time.json | 13 +- .../test_genesis_custom_validator_set.json | 21 +- .../test_genesis_default_configuration.json | 13 +- .../test_invalid_signature.json | 20 +- ...test_proposer_and_attester_signatures.json | 305 ++++ .../test_proposer_signature.json | 263 ++++ .../test_mixed_valid_invalid_signatures.json | 491 ------ ...test_proposer_and_attester_signatures.json | 1313 ----------------- .../test_proposer_signature.json | 1271 ---------------- 30 files changed, 883 insertions(+), 3469 deletions(-) delete mode 100644 lean_client/containers/tests/debug_deserialize.rs rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_block_at_large_slot_number.json (74%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_block_extends_deep_chain.json (60%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_block_with_invalid_parent_root.json (80%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_block_with_invalid_proposer.json (78%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_block_with_invalid_state_root.json (77%) create mode 100644 lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_blocks_with_gaps.json (72%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_empty_blocks.json (67%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_empty_blocks_with_missed_slots.json (68%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_linear_chain_multiple_blocks.json (67%) rename lean_client/{tests/test_vectors/test_blocks => test_vectors/state_transition/devnet/state_transition/test_block_processing}/test_process_first_block_after_genesis.json (76%) rename lean_client/{tests/test_vectors => test_vectors/state_transition/devnet/state_transition}/test_genesis/test_genesis_custom_time.json (82%) rename lean_client/{tests/test_vectors => test_vectors/state_transition/devnet/state_transition}/test_genesis/test_genesis_custom_validator_set.json (74%) rename lean_client/{tests/test_vectors => test_vectors/state_transition/devnet/state_transition}/test_genesis/test_genesis_default_configuration.json (81%) rename lean_client/{tests/test_vectors/test_verify_signatures => test_vectors/verify_signatures/devnet/verify_signatures}/test_invalid_signatures/test_invalid_signature.json (70%) create mode 100644 lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json create mode 100644 lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json delete mode 100644 lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_mixed_valid_invalid_signatures.json delete mode 100644 lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json delete mode 100644 lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index d71fc67..970d374 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -916,6 +916,7 @@ dependencies = [ "sha2 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)", "ssz", "ssz_derive", + "test-generator", "typenum", ] diff --git a/lean_client/Cargo.toml b/lean_client/Cargo.toml index cb996ba..21fad13 100644 --- a/lean_client/Cargo.toml +++ b/lean_client/Cargo.toml @@ -41,6 +41,7 @@ tree-hash = "0.4.0" typenum = "1.19" sha2 = "0.10" rand = "0.9" +test-generator = "0.3.1" [workspace.dev-dependencies] rstest = "0.18.2" diff --git a/lean_client/Makefile b/lean_client/Makefile index 1a2c53a..5dee5e1 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -54,7 +54,7 @@ generate-test-vectors: git fetch --depth 1 origin $(LEAN_SPEC_COMMIT) && \ git switch --detach FETCH_HEAD cd spec && uv run fill --clean --fork=devnet - rm -rf ./test_vectors + rm -rf ./test_vectors && mkdir -p ./test_vectors cp -r ./spec/fixtures/consensus/* ./test_vectors/ .PHONY: build diff --git a/lean_client/containers/Cargo.toml b/lean_client/containers/Cargo.toml index 0927f7e..ad85010 100644 --- a/lean_client/containers/Cargo.toml +++ b/lean_client/containers/Cargo.toml @@ -30,3 +30,4 @@ alloy-primitives = "1.5.2" rstest = "0.18" pretty_assertions = "1.4" serde_json = "1.0" +test-generator = { workspace = true } diff --git a/lean_client/containers/tests/debug_deserialize.rs b/lean_client/containers/tests/debug_deserialize.rs deleted file mode 100644 index 1d28df1..0000000 --- a/lean_client/containers/tests/debug_deserialize.rs +++ /dev/null @@ -1,68 +0,0 @@ -use containers::state::State; -use std::fs; - -#[test] -fn debug_deserialize_state() { - let json_content = fs::read_to_string( - "../tests/test_vectors/test_blocks/test_process_first_block_after_genesis.json", - ) - .expect("Failed to read test vector file"); - - // Try to deserialize just to see where it fails - let result: Result = serde_json::from_str(&json_content); - - match result { - Ok(value) => { - println!("✓ JSON is valid"); - - // Try to extract just the pre state - if let Some(tests) = value.as_object() { - if let Some((test_name, test_case)) = tests.iter().next() { - println!("✓ Found test: {}", test_name); - - if let Some(pre) = test_case.get("pre") { - println!("✓ Found pre state"); - - // Try deserializing field by field - if let Some(pre_obj) = pre.as_object() { - for (field_name, field_value) in pre_obj.iter() { - println!("\nTrying to deserialize field: {}", field_name); - println!( - "Field value type: {}", - match field_value { - serde_json::Value::Null => "null", - serde_json::Value::Bool(_) => "bool", - serde_json::Value::Number(_) => "number", - serde_json::Value::String(_) => "string", - serde_json::Value::Array(_) => "array", - serde_json::Value::Object(_) => "object", - } - ); - - if field_value.is_object() { - if let Some(obj) = field_value.as_object() { - println!( - "Object keys: {:?}", - obj.keys().collect::>() - ); - } - } - } - } - - // Now try to deserialize the whole state - let state_result: Result = serde_json::from_value(pre.clone()); - match state_result { - Ok(_) => println!("\n✓ Successfully deserialized State"), - Err(e) => { - println!("\n✗ Failed to deserialize State"); - panic!("Failed to deserialize State: {}", e); - } - } - } - } - } - } - Err(e) => panic!("Invalid JSON: {}", e), - } -} diff --git a/lean_client/containers/tests/main.rs b/lean_client/containers/tests/main.rs index f951ffe..df10566 100644 --- a/lean_client/containers/tests/main.rs +++ b/lean_client/containers/tests/main.rs @@ -1,4 +1,3 @@ // tests/lib - Test entry point -mod debug_deserialize; mod test_vectors; mod unit_tests; diff --git a/lean_client/containers/tests/test_vectors/block_processing.rs b/lean_client/containers/tests/test_vectors/block_processing.rs index e3325cb..ec8606c 100644 --- a/lean_client/containers/tests/test_vectors/block_processing.rs +++ b/lean_client/containers/tests/test_vectors/block_processing.rs @@ -1,90 +1,15 @@ -// Integration test: All block processing test vectors for devnet2 format -// -// NOTE: These tests are currently disabled because the JSON test vector files -// use a wrapper format (e.g., "validators": {"data": [...]}) that doesn't match -// the Rust deserializer expectations (which expects a direct list). -// The test vectors need to be regenerated by leanSpec to match the expected format, -// or the deserialization logic needs to be updated. +//! Integration test: All block processing test vectors for devnet2 format +use std::path::Path; -use super::runner::TestRunner; - -/* -#[test] -#[cfg(feature = "devnet1")] -fn test_process_first_block_after_genesis() { - let test_path = "../tests/test_vectors/test_blocks/test_process_first_block_after_genesis.json"; - TestRunner::run_block_processing_test(test_path) - .expect("test_process_first_block_after_genesis failed"); -} - -#[test] -#[cfg(feature = "devnet1")] -fn test_blocks_with_gaps() { - let test_path = "../tests/test_vectors/test_blocks/test_blocks_with_gaps.json"; - TestRunner::run_block_processing_test(test_path).expect("test_blocks_with_gaps failed"); -} +use test_generator::test_resources; -#[test] -#[cfg(feature = "devnet1")] -fn test_linear_chain_multiple_blocks() { - let test_path = "../tests/test_vectors/test_blocks/test_linear_chain_multiple_blocks.json"; - TestRunner::run_block_processing_test(test_path) - .expect("test_linear_chain_multiple_blocks failed"); -} - -#[test] -#[cfg(feature = "devnet1")] -fn test_block_extends_deep_chain() { - let test_path = "../tests/test_vectors/test_blocks/test_block_extends_deep_chain.json"; - TestRunner::run_block_processing_test(test_path).expect("test_block_extends_deep_chain failed"); -} - -#[test] -#[cfg(feature = "devnet1")] -fn test_empty_blocks() { - let test_path = "../tests/test_vectors/test_blocks/test_empty_blocks.json"; - TestRunner::run_block_processing_test(test_path).expect("test_empty_blocks failed"); -} - -#[test] -#[cfg(feature = "devnet1")] -fn test_empty_blocks_with_missed_slots() { - let test_path = "../tests/test_vectors/test_blocks/test_empty_blocks_with_missed_slots.json"; - TestRunner::run_block_processing_test(test_path) - .expect("test_empty_blocks_with_missed_slots failed"); -} - -#[test] -#[cfg(feature = "devnet1")] -fn test_block_at_large_slot_number() { - let test_path = "../tests/test_vectors/test_blocks/test_block_at_large_slot_number.json"; - TestRunner::run_block_processing_test(test_path) - .expect("test_block_at_large_slot_number failed"); -} - -// Invalid block tests (expecting failures) - -#[test] -#[cfg(feature = "devnet1")] -fn test_block_with_invalid_parent_root() { - let test_path = "../tests/test_vectors/test_blocks/test_block_with_invalid_parent_root.json"; - TestRunner::run_block_processing_test(test_path) - .expect("test_block_with_invalid_parent_root failed"); -} +use super::runner::TestRunner; -#[test] -#[cfg(feature = "devnet1")] -fn test_block_with_invalid_proposer() { - let test_path = "../tests/test_vectors/test_blocks/test_block_with_invalid_proposer.json"; - TestRunner::run_block_processing_test(test_path) - .expect("test_block_with_invalid_proposer failed"); -} +#[test_resources("test_vectors/state_transition/*/state_transition/test_block_processing/*.json")] +fn block_processing(spec_file: &str) { + let test_path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join(spec_file); -#[test] -#[cfg(feature = "devnet1")] -fn test_block_with_invalid_state_root() { - let test_path = "../tests/test_vectors/test_blocks/test_block_with_invalid_state_root.json"; - TestRunner::run_block_processing_test(test_path) - .expect("test_block_with_invalid_state_root failed"); + TestRunner::run_block_processing_test(test_path).unwrap(); } -*/ diff --git a/lean_client/containers/tests/test_vectors/genesis.rs b/lean_client/containers/tests/test_vectors/genesis.rs index 92acf25..80bd1fe 100644 --- a/lean_client/containers/tests/test_vectors/genesis.rs +++ b/lean_client/containers/tests/test_vectors/genesis.rs @@ -1,20 +1,15 @@ -// Integration test: Genesis state test vectors -use super::runner::TestRunner; +//! Integration test: Genesis state test vectors +use std::path::Path; -#[test] -fn test_genesis_default_configuration() { - let test_path = "../tests/test_vectors/test_genesis/test_genesis_default_configuration.json"; - TestRunner::run_genesis_test(test_path).expect("test_genesis_default_configuration failed"); -} +use test_generator::test_resources; -#[test] -fn test_genesis_custom_time() { - let test_path = "../tests/test_vectors/test_genesis/test_genesis_custom_time.json"; - TestRunner::run_genesis_test(test_path).expect("test_genesis_custom_time failed"); -} +use super::runner::TestRunner; + +#[test_resources("test_vectors/state_transition/*/state_transition/test_genesis/*.json")] +fn genesis(spec_file: &str) { + let test_path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join(spec_file); -#[test] -fn test_genesis_custom_validator_set() { - let test_path = "../tests/test_vectors/test_genesis/test_genesis_custom_validator_set.json"; - TestRunner::run_genesis_test(test_path).expect("test_genesis_custom_validator_set failed"); + TestRunner::run_genesis_test(test_path).unwrap(); } diff --git a/lean_client/containers/tests/test_vectors/verify_signatures.rs b/lean_client/containers/tests/test_vectors/verify_signatures.rs index 13692f7..6887bd6 100644 --- a/lean_client/containers/tests/test_vectors/verify_signatures.rs +++ b/lean_client/containers/tests/test_vectors/verify_signatures.rs @@ -1,52 +1,25 @@ -// Integration test: verify_signatures test vectors -// Tests XMSS signature verification on SignedBlockWithAttestation -// -// NOTE: Without the `xmss-verify` feature, signature verification only checks -// structure (attestation count matches signature count, validator indices valid). -// Full cryptographic verification requires `--features xmss-verify`. -// -// IMPORTANT: There is currently a configuration mismatch between leanSpec Python -// (HASH_LEN_FE=8, 52-byte pubkeys) and leansig Rust (HASH_LEN_FE=7, 48-byte pubkeys). -// Until this is resolved, the xmss-verify tests will fail with "Invalid public key length". -use super::runner::TestRunner; - -// Valid signature tests -// These tests verify that properly signed blocks pass verification. -// Without xmss-verify feature, they pass because structural validation succeeds. +//! Integration test: verify_signatures test vectors +//! Tests XMSS signature verification on SignedBlockWithAttestation +//! +//! NOTE: Without the `xmss-verify` feature, signature verification only checks +//! structure (attestation count matches signature count, validator indices valid). +//! Full cryptographic verification requires `--features xmss-verify`. +//! +//! IMPORTANT: There is currently a configuration mismatch between leanSpec Python +//! (HASH_LEN_FE=8, 52-byte pubkeys) and leansig Rust (HASH_LEN_FE=7, 48-byte pubkeys). +//! Until this is resolved, the xmss-verify tests will fail with "Invalid public key length". -#[test] -#[cfg(feature = "devnet1")] -fn test_proposer_signature() { - let test_path = "../tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json"; - TestRunner::run_verify_signatures_test(test_path).expect("test_proposer_signature failed"); -} +use std::path::Path; -#[test] -#[cfg(feature = "devnet1")] -fn test_proposer_and_attester_signatures() { - let test_path = "../tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json"; - TestRunner::run_verify_signatures_test(test_path) - .expect("test_proposer_and_attester_signatures failed"); -} +use test_generator::test_resources; -// Invalid signature tests (expecting verification failure) -// NOTE: These tests are ignored by default because without the `xmss-verify` feature, -// signature verification doesn't actually check cryptographic validity. -// Run with `cargo test --features xmss-verify` to enable full signature verification. +use super::runner::TestRunner; -#[test] -#[cfg(feature = "devnet1")] -#[ignore = "Requires xmss-verify feature for actual signature validation. Run with: cargo test --features xmss-verify"] -fn test_invalid_signature() { - let test_path = "../tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json"; - TestRunner::run_verify_signatures_test(test_path).expect("test_invalid_signature failed"); -} +#[test_resources("test_vectors/verify_signatures/*/verify_signatures/*/*.json")] +fn verify_signatures(spec_file: &str) { + let test_path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join(spec_file); -#[test] -#[cfg(feature = "devnet1")] -#[ignore = "Requires xmss-verify feature for actual signature validation. Run with: cargo test --features xmss-verify"] -fn test_mixed_valid_invalid_signatures() { - let test_path = "../tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_mixed_valid_invalid_signatures.json"; - TestRunner::run_verify_signatures_test(test_path) - .expect("test_mixed_valid_invalid_signatures failed"); + TestRunner::run_verify_signatures_test(test_path).unwrap(); } diff --git a/lean_client/fork_choice/Cargo.toml b/lean_client/fork_choice/Cargo.toml index ebd5b18..c50bd99 100644 --- a/lean_client/fork_choice/Cargo.toml +++ b/lean_client/fork_choice/Cargo.toml @@ -14,4 +14,4 @@ ssz = { workspace = true } [dev-dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -test-generator = "0.3.1" +test-generator = { workspace = true } diff --git a/lean_client/tests/test_vectors/test_blocks/test_block_at_large_slot_number.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json similarity index 74% rename from lean_client/tests/test_vectors/test_blocks/test_block_at_large_slot_number.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json index 359539e..92e3cb7 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_block_at_large_slot_number.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_at_large_slot_number[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_at_large_slot_number[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,8 +59,8 @@ { "slot": 100, "proposerIndex": 0, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x508c4570ec13b95d2d1db96378d175b3a4a849e74802a9187a841d6e7a54b660", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x6f9b7a10879510ab1e1c4246d655cd08a7a1db491580a20c47eb019c08e54f50", "body": { "attestations": { "data": [] @@ -71,7 +72,7 @@ "slot": 100 }, "_info": { - "hash": "0x9fca4824ebb0e2b5b70ac11bee957abad7e0eb41ee1488b7cecce932ce420c92", + "hash": "0x792bfdf069ad579de1f37e34db90c37bfb66082c866c7afa390626fa7b9a166f", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_at_large_slot_number[fork_Devnet]", "description": "Test block processing at high slot numbers.\n\n Scenario\n --------\n Jump directly from genesis to slot 100, simulating:\n - Network bootstrap after long downtime\n - Test environment with artificial time jump\n - Integer overflow boundary testing\n\n Expected Behavior\n -----------------\n 1. Process 99 empty slots: 1\u21922\u2192...\u219299\u2192100\n 2. Block at slot 100 processes correctly\n 3. No integer overflow or wraparound\n 4. State remains consistent", diff --git a/lean_client/tests/test_vectors/test_blocks/test_block_extends_deep_chain.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json similarity index 60% rename from lean_client/tests/test_vectors/test_blocks/test_block_extends_deep_chain.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json index 8494dd5..16d3aab 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_block_extends_deep_chain.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_extends_deep_chain[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_extends_deep_chain[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -69,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -80,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -91,8 +92,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -102,8 +103,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -113,8 +114,8 @@ { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", - "stateRoot": "0x3b523ede40789338d432b631a65579b3855c2861c90c44cf760e9e9bc6413cdc", + "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", "body": { "attestations": { "data": [] @@ -124,8 +125,8 @@ { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x905c0955933db9009e7ddd2fc2a4dd8840fb336c9a919edaca843167706be137", - "stateRoot": "0xf1f50a275eee0692aa7c36132d2ee64ddecfa4f95c59627d723a4404a7ff4382", + "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", "body": { "attestations": { "data": [] @@ -135,8 +136,8 @@ { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x4c785749defd50f10c82c131fd959d7b9879ea6f1f0821c5f43d4ee739e6c949", - "stateRoot": "0xa90ee765af23b009dff6abf61532da66fc61f2d42940ecca2d87cbfa5916e886", + "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", "body": { "attestations": { "data": [] @@ -146,8 +147,8 @@ { "slot": 9, "proposerIndex": 1, - "parentRoot": "0xc7fdc655ebd7ded1d1bf2be680b36741999f95c1faf8ebc64fa5c06f456a142c", - "stateRoot": "0x744b3ff0f2f4e429f580fdce378b10931435994228b4017d753823db3baf052e", + "parentRoot": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "stateRoot": "0xc8f2880bc8e3d1ba655c75f34ae5a20555503ce8296cbc3a2a266fbcdb7b078b", "body": { "attestations": { "data": [] @@ -157,8 +158,8 @@ { "slot": 10, "proposerIndex": 2, - "parentRoot": "0xbdf3561bf5f71dd4607d6c45a715b07a5f7e8c0bbfaf4e7d259b8f19338520c3", - "stateRoot": "0x755bbab4584387db2837851ea71584ea97033a88703a899a08b2c8a263aa0659", + "parentRoot": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "stateRoot": "0x054571db4eb9706f6f29a97bffaa00765b2a75b77ad5812e8619066e942d3192", "body": { "attestations": { "data": [] @@ -168,8 +169,8 @@ { "slot": 11, "proposerIndex": 3, - "parentRoot": "0x8d926eb6a1d640269c0b110d7c4d3209f954bfc6aa122ea6ca4a6b6513c6e082", - "stateRoot": "0x184cda538136a8062143a4045f35bfa114b60b15e00de3ed57994db03ce2c3b3", + "parentRoot": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "stateRoot": "0x4a359bc7767356dd13c761cc98bd9f0a62013636c0395e9babd5d37794c9c728", "body": { "attestations": { "data": [] @@ -179,8 +180,8 @@ { "slot": 12, "proposerIndex": 0, - "parentRoot": "0x23a7d96f526ad777525e719193f92695fd82096e80dc7daf8255bb42b7ca2c77", - "stateRoot": "0x58d8a219d785385ea9b40a75442280464e8248444ee425537f4d6f4065dd35b2", + "parentRoot": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "stateRoot": "0x584b4ae5faaeb8a0cc6b190b4f570d773fc3f40426c0eb5a1f1730b199633b0a", "body": { "attestations": { "data": [] @@ -190,8 +191,8 @@ { "slot": 13, "proposerIndex": 1, - "parentRoot": "0x322d98625bd532ced9311e0a00581c65d553a7f92caf59f6e1e65404176913ae", - "stateRoot": "0xf120a140fa1b513dccd0a69c7904098f0e32a7fdb23e9c1eea79cae231c29964", + "parentRoot": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "stateRoot": "0x4f3ed1bd4a625037ebbfdcf9b32a384ca5233b432df7e421f1d09af26d2bd841", "body": { "attestations": { "data": [] @@ -201,8 +202,8 @@ { "slot": 14, "proposerIndex": 2, - "parentRoot": "0x40148bdc39de21bc53cd64a6b55ce7a9b47daddcc12e0544ec3a6cba908a20ba", - "stateRoot": "0x6cda9c51f604bc376a0eb34dc70f9fadc6b4120e1702f607a769f9f71f4b1b8e", + "parentRoot": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "stateRoot": "0x638ae62be9308adae58e1239e365a27d053727985b6323652cb66c8a84926e88", "body": { "attestations": { "data": [] @@ -212,8 +213,8 @@ { "slot": 15, "proposerIndex": 3, - "parentRoot": "0x92124f570e1e80a18be895b6c09e7914f35426835d15520df8f3aa5ba78e3792", - "stateRoot": "0x816eedba680d484a5a4476e550f4be20411772b3abbe3281326ee6b8c96ca06b", + "parentRoot": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "stateRoot": "0x0792f723312433e078a11523609eb9b90a961d0ce1346717f86dfb56a09712ca", "body": { "attestations": { "data": [] @@ -223,8 +224,8 @@ { "slot": 16, "proposerIndex": 0, - "parentRoot": "0x861730af0d30d56cec4ad72fb9976a0ab4d6b8e530813f3f89da6d3b577ff1ea", - "stateRoot": "0xbd40ed0d00a8319c7bbc0b9170524733bb5cd6ef46b273dd09f41f90fc415d95", + "parentRoot": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "stateRoot": "0x76f71fb8340d28f209436d7f5a46d5ab47f6200ce78e98028869edcc17306c36", "body": { "attestations": { "data": [] @@ -234,8 +235,8 @@ { "slot": 17, "proposerIndex": 1, - "parentRoot": "0x0b1512f9e9ef5eeb0227be82ec5ec76dcb4118a26134276bf39d7bb7b177bdd2", - "stateRoot": "0xd1273f3339f0b990ce3ea696a73a99a0f027b679cd4fe42fd0a4addd3694327b", + "parentRoot": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "stateRoot": "0x73fb22f47f7645fe9c7484b48946bdcc9feefb77c89119cac4ebfbd897efdf05", "body": { "attestations": { "data": [] @@ -245,8 +246,8 @@ { "slot": 18, "proposerIndex": 2, - "parentRoot": "0xac20db17803c5cd6389f5c918eeb2633508c8f1dd7d94e192c236eb984b0307c", - "stateRoot": "0xbb6bf50f37821af5dbe71118e7f9644ebeff968760fa8dc33d3291710350e433", + "parentRoot": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "stateRoot": "0x304172e1836eede1f7a460cd0466152ea27449ced669a4e42133579f86d32640", "body": { "attestations": { "data": [] @@ -256,8 +257,8 @@ { "slot": 19, "proposerIndex": 3, - "parentRoot": "0xb9325fc8ca268c606376cc8bd6188bc010d3cf8b9c0ac8851d28f258f0f810c9", - "stateRoot": "0x595d5f28c6f0184f9c4768263fb735b5579806ea4881bcca3e7d04c4bb62dff5", + "parentRoot": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "stateRoot": "0xb0bfc653ee2bcbe154796b6f0449d89bb63b090444b361e9a86eaccc74898327", "body": { "attestations": { "data": [] @@ -267,8 +268,8 @@ { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x9a702ffcac8fe110e21d3f014155bdef22b9a42e5742109fd412a7fe84c2ed07", - "stateRoot": "0x97640dc053b26e03933d4b601cdfbf3fae9022a9ead0c3ce5edef9c9af2a9214", + "parentRoot": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "stateRoot": "0x086dd2f62898dc60b1c7a964f30ee820c2fcb855c06aab1db1df6a7bce28690b", "body": { "attestations": { "data": [] @@ -280,7 +281,7 @@ "slot": 20 }, "_info": { - "hash": "0x9c58f70c0ed02cdcd06cb0845cdeb6280210a13707c2d9c8acf1cad3fb0e0731", + "hash": "0xec6542115b0f17f95f10889d3979d360390d3ad83ace61f5e9beb6aa80b3d447", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_extends_deep_chain[fork_Devnet]", "description": "Test that blocks can extend already-deep chains.\n\n Scenario\n --------\n Build a 20-block chain to simulate a mature blockchain state,\n then verify new blocks can still extend it correctly.\n\n Expected Behavior\n -----------------\n 1. All 20 blocks process successfully\n 2. Parent linkage maintained throughout\n 3. State advances to slot 20\n 4. Historical roots accumulate correctly\n 5. No degradation in processing", diff --git a/lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_parent_root.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json similarity index 80% rename from lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_parent_root.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json index 33bf998..60b535c 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_parent_root.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_parent_root[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_parent_root[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -69,7 +70,7 @@ ], "expectException": "AssertionError", "_info": { - "hash": "0xaa92f50f9e8ca9bc35984f150b886a2f15b8e977f5ef9cc82cbbf0cc0fa94edb", + "hash": "0xa69cd326fd14a060fb9e770eff2407fa1d418245b188c6ef976df87acee884a5", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_parent_root[fork_Devnet]", "description": "Test that blocks with wrong parent root are rejected.\n\n Scenario\n --------\n Attempt to process a block where parent root doesn't match\n hash_tree_root(state.latest block header).\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Block parent root mismatch\"\n\n Why This Matters\n ----------------\n Maintains chain integrity:\n - Blocks must reference correct parent\n - Prevents chain history forgery\n - Ensures linear chain continuity\n - Critical for fork resolution\n\n Without this check, attackers could create invalid chain branches.", diff --git a/lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_proposer.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json similarity index 78% rename from lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_proposer.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json index cb19d25..06ff9a6 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_proposer.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_proposer[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_proposer[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ { "slot": 1, "proposerIndex": 3, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "body": { "attestations": { @@ -69,7 +70,7 @@ ], "expectException": "AssertionError", "_info": { - "hash": "0xde1af5c74196e1a96deaa0e33e8dae9cb369901ac5ff2bd4c273445c79b3443b", + "hash": "0x4072c329bcc00c6b8ab9c2e5f6fa55618f598635eb7002a6164ae122d551e11c", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_proposer[fork_Devnet]", "description": "Test that blocks from wrong proposer are rejected.\n\n Scenario\n --------\n Attempt to process a block where proposer index doesn't match\n the expected proposer for that slot.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Incorrect block proposer\"\n\n Why This Matters\n ----------------\n Prevents unauthorized block production:\n - Only designated proposer can produce blocks\n - Prevents validator impersonation\n - Maintains protocol security\n - Essential for consensus integrity\n\n Without this check, any validator could produce blocks for any slot.", diff --git a/lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_state_root.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json similarity index 77% rename from lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_state_root.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json index 4a76f8d..1c3db2d 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_block_with_invalid_state_root.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_state_root[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_state_root[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,7 +59,7 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", "stateRoot": "0xbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaad", "body": { "attestations": { @@ -69,7 +70,7 @@ ], "expectException": "AssertionError", "_info": { - "hash": "0x2eefa993f2ea8e65a529e6e41c0ee3d6cb501e4bd9a98d003a6f72f6e473d944", + "hash": "0x5e785147cab337f4e01e3a8280a3582ba87d607bb348823bc769e76e818c1980", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_state_root[fork_Devnet]", "description": "Test that blocks with wrong state root commitment are rejected.\n\n Scenario\n --------\n Create a block with state root that doesn't match the actual\n post-state hash.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Invalid block state root\"\n\n Why This Matters\n ----------------\n Cryptographic state commitment is fundamental:\n - Proves correct state execution\n - Prevents state manipulation\n\n This is a critical validation - without it, proposers could claim any arbitrary state.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json new file mode 100644 index 0000000..4c81ae5 --- /dev/null +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json @@ -0,0 +1,81 @@ +{ + "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_wrong_slot[fork_Devnet][fork_devnet-state_transition_test]": { + "network": "Devnet", + "leanEnv": "test", + "pre": { + "config": { + "genesisTime": 0 + }, + "slot": 1, + "latestBlockHeader": { + "slot": 0, + "proposerIndex": 0, + "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" + }, + "latestJustified": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "latestFinalized": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "historicalBlockHashes": { + "data": [] + }, + "justifiedSlots": { + "data": [] + }, + "validators": { + "data": [ + { + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "index": 0 + }, + { + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "index": 1 + }, + { + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "index": 2 + }, + { + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "index": 3 + } + ] + }, + "justificationsRoots": { + "data": [] + }, + "justificationsValidators": { + "data": [] + } + }, + "blocks": [ + { + "slot": 2, + "proposerIndex": 2, + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "body": { + "attestations": { + "data": [] + } + } + } + ], + "expectException": "AssertionError", + "expectExceptionMessage": "Block slot mismatch", + "_info": { + "hash": "0x3e9a6c55ea851ac9514f3db8a433d571552638e884a3cbc64b10c2966f14fa24", + "comment": "`leanSpec` generated test", + "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_wrong_slot[fork_Devnet]", + "description": "Test that blocks with mismatched slot are rejected.\n\n Scenario\n --------\n Attempt to process a block at slot 1, but the block claims to be\n at slot 2.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Block slot mismatch\"\n\n Why This Matters\n ----------------\n Ensures temporal consistency:\n - Blocks can't lie about their slot\n - Prevents time manipulation attacks\n - Maintains protocol timing integrity\n - Essential for slot-based consensus", + "fixtureFormat": "state_transition_test" + } + } +} \ No newline at end of file diff --git a/lean_client/tests/test_vectors/test_blocks/test_blocks_with_gaps.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json similarity index 72% rename from lean_client/tests/test_vectors/test_blocks/test_blocks_with_gaps.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json index 440ff91..ebbd53e 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_blocks_with_gaps.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_blocks_with_gaps[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_blocks_with_gaps[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -69,8 +70,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0x580f44525ca72dad52351f78f0cab0081fefa16ab07b8c7d81f68df89cea7e7f", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0x789b378042d8d7c40826e8a4ac9d56b475824914fd6064e9d89a700502cd8d6e", "body": { "attestations": { "data": [] @@ -80,8 +81,8 @@ { "slot": 8, "proposerIndex": 0, - "parentRoot": "0xf4cbe7bf5f56a604059857975ff56fe364ddf470249262a1908f11cac96687e1", - "stateRoot": "0x5ef55253cb56a389c3db5bf55fc09801cb5c7ee78c00e68a3bd2273d7c5a2fe7", + "parentRoot": "0x0cd63b486d1dd15e47fed3c75d4b607fff03b0c384d2eeb0359d8a602ff9d8d4", + "stateRoot": "0x03c27bd408b531f462b5266bd73bc74681eaab69b9f6b209519faa6c0d0ecc09", "body": { "attestations": { "data": [] @@ -96,7 +97,7 @@ "historicalBlockHashesCount": 8 }, "_info": { - "hash": "0x3d776baee05fb062776f68b9dc498aca6051e7b145e58b9e86e9aa8734480126", + "hash": "0x6a5933287f43f90459dbc020c904e6a5b309adb8dd56f65dd5f422ce818f03f9", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_blocks_with_gaps[fork_Devnet]", "description": "Test blocks separated by empty slots.\n\n Scenario\n --------\n Build chain with gaps:\n - Slot 1: Block\n - Slots 2-3: Empty\n - Slot 4: Block\n - Slots 5-7: Empty\n - Slot 8: Block\n\n Expected Behavior\n -----------------\n 1. Blocks process at specified slots\n 2. Empty slots handled automatically\n 3. Parent linkage spans gaps correctly\n 4. State advances to slot 8\n\n Why This Matters\n ----------------\n Missed proposals are common:\n - Validators offline\n - Network partitions\n - Missed attestations\n\n This validates resilience to gaps.", diff --git a/lean_client/tests/test_vectors/test_blocks/test_empty_blocks.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json similarity index 67% rename from lean_client/tests/test_vectors/test_blocks/test_empty_blocks.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json index 6b50682..310e089 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_empty_blocks.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -69,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -80,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -91,8 +92,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -102,8 +103,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -113,8 +114,8 @@ { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x785cda184b3905febc2c61321f1235c42b141d2c15400ef4cda26829a2fc3f74", - "stateRoot": "0x3b523ede40789338d432b631a65579b3855c2861c90c44cf760e9e9bc6413cdc", + "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", "body": { "attestations": { "data": [] @@ -128,7 +129,7 @@ "historicalBlockHashesCount": 6 }, "_info": { - "hash": "0x4468a1914f8fdd058efd8e8675c1dae8070149daf7973f803dd98eb26fb24c01", + "hash": "0x56feeb1d2a8ff7a3eec36c167d794aaea4f996643bce794940dfc44db01b6ff9", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks[fork_Devnet]", "description": "Test processing blocks with empty body (no attestations).\n\n Scenario\n --------\n Build chain of blocks with empty body:\n - Slot 1: Block, Empty body\n - Slot 2: Block, Empty body\n - Slot 3: Block, Empty body\n - Slot 4: Block, Empty body\n - Slot 5: Block, Empty body\n - Slot 6: Block, Empty body\n\n Expected Behavior\n -----------------\n 1. Blocks process as expected\n 2. State advances to slot 6", diff --git a/lean_client/tests/test_vectors/test_blocks/test_empty_blocks_with_missed_slots.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json similarity index 68% rename from lean_client/tests/test_vectors/test_blocks/test_empty_blocks_with_missed_slots.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json index 9f57064..a756efb 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_empty_blocks_with_missed_slots.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks_with_missed_slots[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks_with_missed_slots[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -69,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -80,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -91,8 +92,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0xbaf197fcdcb8dd839b93fef6314dbeaac1cd55783f805002c4e8b5ced454f42e", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0x6c26076f86536098f57f9a530b9a58873498c5bd7173cccb220b9e0a4219871d", "body": { "attestations": { "data": [] @@ -102,8 +103,8 @@ { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x66403fc3ff2488e54ea0e8796746a48e3591ba8f78954db7b06e0ff93a609801", - "stateRoot": "0x60422241872bfe48fb1e1515571522896e50bb18ab6db44f8d129f585af0804c", + "parentRoot": "0x041eee0ddc77a228175d6ff90c00a8ede62ede3431a90f54e452d4b7a17a5ee2", + "stateRoot": "0x768566c1db94161383b795b29fc4e5e470af9e476fe2c0e88d289f4b52185a81", "body": { "attestations": { "data": [] @@ -117,7 +118,7 @@ "historicalBlockHashesCount": 6 }, "_info": { - "hash": "0x39d5ef697e5b51d473370caf9ec6ea1562afc2df534484ae01d6aabb9636955b", + "hash": "0xb858e8ad3f7dd0ca7b079bc5b9f2472626f277572b055d938f8d0d683f2da30b", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks_with_missed_slots[fork_Devnet]", "description": "Test processing blocks with empty body (no attestations) combined with missed slots.\n\n Scenario\n --------\n Build chain of blocks with empty body + missed slot:\n - Slot 1: Block\n - Slot 2: Block, Empty body\n - Slot 3: BLock, Empty body\n - Slot 4: Missed\n - Slot 5: Block, Empty body\n - Slot 6: Block\n\n Expected Behavior\n -----------------\n 1. Blocks process at specified slots\n 2. Empty slots handled automatically\n 3. State advances to slot 6", diff --git a/lean_client/tests/test_vectors/test_blocks/test_linear_chain_multiple_blocks.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json similarity index 67% rename from lean_client/tests/test_vectors/test_blocks/test_linear_chain_multiple_blocks.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json index 6bd88a3..5171fd3 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_linear_chain_multiple_blocks.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_linear_chain_multiple_blocks[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_linear_chain_multiple_blocks[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -69,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xf0e2cc55723ca299895cc78dad0648a35d2e3f708a1f8167a4ba558166737ae9", - "stateRoot": "0xa70e680f4b7f39ee3ef9c080690bb940473c3a3266f870ba11eec671520059fa", + "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", "body": { "attestations": { "data": [] @@ -80,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xb30b965597442897d61ce6f3af248f2a69970bbdae257523a48f057bb05e7edf", - "stateRoot": "0x0e56faa2e9e1eb1bf6c392e75fca918bc964cadf507b22c798acd3f25146b43e", + "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", "body": { "attestations": { "data": [] @@ -91,8 +92,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x68c8624259868b339c77d811611e8e2e3c02f937f16094af68c58967799ab933", - "stateRoot": "0x8ac92be5b08b951d92e3aaea2b2fce30c976de51d82d84dfe992f27110329359", + "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", "body": { "attestations": { "data": [] @@ -102,8 +103,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x239743320b5e1f0eb98fa47bd60f8778fbd5727a6dee6dee71d9c87b2db0e17d", - "stateRoot": "0x7fb031793311af62c3ed91f0f0a5f7095ad584b30fd18c62e16d481733d214eb", + "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", "body": { "attestations": { "data": [] @@ -115,7 +116,7 @@ "slot": 5 }, "_info": { - "hash": "0x156cbb554fc46290c0566fb5741af89fe8085b85347ad580d555d1dd43b7c7ad", + "hash": "0xb3347c2521c58f977e4b0810fa04762675de07d33d3359b36cb1de57943f815c", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_linear_chain_multiple_blocks[fork_Devnet]", "description": "Test building a linear chain of multiple blocks.\n\n Scenario\n --------\n Build a 5-block linear chain:\n genesis \u2192 block1 \u2192 block2 \u2192 block3 \u2192 block4 \u2192 block5\n\n Expected Behavior\n -----------------\n 1. Each block processes in sequence\n 2. Parent linkage maintained throughout\n 3. State advances monotonically\n 4. Historical roots accumulate\n 5. Final state at slot 5", diff --git a/lean_client/tests/test_vectors/test_blocks/test_process_first_block_after_genesis.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json similarity index 76% rename from lean_client/tests/test_vectors/test_blocks/test_process_first_block_after_genesis.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json index 28054f9..a5c42c0 100644 --- a/lean_client/tests/test_vectors/test_blocks/test_process_first_block_after_genesis.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_block_processing.py::test_process_first_block_after_genesis[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_block_processing.py::test_process_first_block_after_genesis[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -58,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xb0379c64780b02bee1b0005f65ae1f79c55c67f7a8aa1cb1f243c7dd46714be1", - "stateRoot": "0x1e2a71ba98d42232a279202b449702bac405858921073a2bab416c2024236a1e", + "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", "body": { "attestations": { "data": [] @@ -74,7 +75,7 @@ "historicalBlockHashesCount": 1 }, "_info": { - "hash": "0x2229402f1eedaca3ffb519105154df91832c2369744821fbe2cc8e353fe08a7b", + "hash": "0xd1bdc1835e365b2a98c5c31169bc4db5357cc21d1d81a16e935eb2028e12749b", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_process_first_block_after_genesis[fork_Devnet]", "description": "Test processing the first block after genesis.\n\n Scenario\n --------\n Process a single block at slot 1 immediately after genesis.\n\n Expected Behavior\n -----------------\n 1. State advances from slot 0 to slot 1\n 2. Block header is validated and processed\n 3. Latest block header updated to new block\n 4. Historical roots updated with genesis\n 5. Post-state at slot 1\n\n This is the foundation for all subsequent blocks.", diff --git a/lean_client/tests/test_vectors/test_genesis/test_genesis_custom_time.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json similarity index 82% rename from lean_client/tests/test_vectors/test_genesis/test_genesis_custom_time.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json index e7094c9..72d1126 100644 --- a/lean_client/tests/test_vectors/test_genesis/test_genesis_custom_time.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_time[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_time[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 1234567890 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -82,7 +83,7 @@ } }, "_info": { - "hash": "0x18ff784d32fe3dbc542d4683afc605f6ffe3132bab348ce62d0e177490a76b36", + "hash": "0x1676cd789fda952b19f0592c94b020bf7a43523aab0951e65d17c13ec5241f12", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_time[fork_Devnet]", "description": "Test genesis state with custom genesis time.\n\n Scenario\n --------\n Generate a genesis state with:\n - genesis_time = 1234567890\n - Default 4 validators\n\n Expected Behavior\n -----------------\n Genesis state should respect the custom genesis time while\n maintaining all other genesis properties.", diff --git a/lean_client/tests/test_vectors/test_genesis/test_genesis_custom_validator_set.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json similarity index 74% rename from lean_client/tests/test_vectors/test_genesis/test_genesis_custom_validator_set.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json index 92e0594..f4d9dd6 100644 --- a/lean_client/tests/test_vectors/test_genesis/test_genesis_custom_validator_set.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_validator_set[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_validator_set[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,35 +31,35 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 }, { - "pubkey": "0xe9c00205c556b91d56cb8e5bd9427b3a3bd5b32aef243616c8af133b9a9b6f37c428ce3870fd3b231c960e26785cb3109e0fc115", + "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", "index": 4 }, { - "pubkey": "0x35b47f3cb6239410ea02ed3d7af8c26822d32637383a0f64190722244ba7b54b412e016720db4d46d26d16656c7e402cda2ae557", + "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", "index": 5 }, { - "pubkey": "0x984c9749be3d147502b7961e7ff2422fbdfab85b9ddff5158d03b140f9ff371252475a0c54d1d1709f543b48dc475f0976958708", + "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", "index": 6 }, { - "pubkey": "0x730c97594267372a9512f76b600edc22e6ef65429f96d311d16264724ab0db1badc68f76390f54187c2557568bbe5e3cdc800364", + "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", "index": 7 } ] @@ -98,7 +99,7 @@ } }, "_info": { - "hash": "0x40a70d12895f8b35b35f19606dd4bcad25d288340b9e09f914729d869913f611", + "hash": "0xeb9c32040335d481228d18e590c01445d4e545d953d55cd14121013bd438430d", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_validator_set[fork_Devnet]", "description": "Test genesis state with custom validator set.\n\n Scenario\n --------\n Generate a genesis state with:\n - 8 validators instead of default 4\n - Custom validator pubkeys\n\n Expected Behavior\n -----------------\n Genesis state should contain exactly 8 validators while\n maintaining all other genesis properties.", diff --git a/lean_client/tests/test_vectors/test_genesis/test_genesis_default_configuration.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json similarity index 81% rename from lean_client/tests/test_vectors/test_genesis/test_genesis_default_configuration.json rename to lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json index bed6bb3..3662f9b 100644 --- a/lean_client/tests/test_vectors/test_genesis/test_genesis_default_configuration.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json @@ -1,6 +1,7 @@ { - "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_default_configuration[fork_Devnet][fork_Devnet-state_transition_test]": { + "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_default_configuration[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", + "leanEnv": "test", "pre": { "config": { "genesisTime": 0 @@ -30,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 }, { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", "index": 1 }, { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", "index": 2 }, { - "pubkey": "0xb85c660a781e825a1b783822984e894c4558e304a4b71307f095821d92b6351197c3b1485ffcf872c9a13d5c9381b1636eeed359", + "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", "index": 3 } ] @@ -82,7 +83,7 @@ } }, "_info": { - "hash": "0xe9da7307359445f817b7e3f7274a65b4b2521e055211739acd4456a3c1c845ba", + "hash": "0xdaef5b4f74195028f564a2f9d827ed277fff97c5127c208440b06640008b522e", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_default_configuration[fork_Devnet]", "description": "Test genesis state with default configuration.\n\n Scenario\n --------\n Generate a genesis state with default parameters:\n - genesis_time = 0\n - 4 validators with zero pubkeys", diff --git a/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json similarity index 70% rename from lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json rename to lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json index ef3fcf3..773a283 100644 --- a/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_invalid_signature.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json @@ -1,7 +1,7 @@ { - "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet][fork_Devnet-verify_signatures_test]": { + "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet][fork_devnet-verify_signatures_test]": { "network": "Devnet", - "leanEnv": "prod", + "leanEnv": "test", "anchorState": { "config": { "genesisTime": 0 @@ -31,7 +31,7 @@ "validators": { "data": [ { - "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", "index": 0 } ] @@ -48,8 +48,8 @@ "block": { "slot": 1, "proposerIndex": 0, - "parentRoot": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", - "stateRoot": "0x38d7edaf9c08ffb285eb3e1e64456fbe430d4c4a4bab9b0bf29565b74da5c620", + "parentRoot": "0xb6f6683bf01027dd60f095f477b8ca38fbe23c18eb02f0aed0b2f34b7a4584f0", + "stateRoot": "0x6c53c1d71203251ceb0abcb1d14a34b53c0760abc1b1ac51d509cbe16d8ceb16", "body": { "attestations": { "data": [] @@ -61,15 +61,15 @@ "data": { "slot": 1, "head": { - "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", + "root": "0xcdc120c88d251c898fc9b9a1f091b0cb108d9ac82d2784029917c2ac3cee82ee", "slot": 1 }, "target": { - "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", + "root": "0xcdc120c88d251c898fc9b9a1f091b0cb108d9ac82d2784029917c2ac3cee82ee", "slot": 1 }, "source": { - "root": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", + "root": "0xb6f6683bf01027dd60f095f477b8ca38fbe23c18eb02f0aed0b2f34b7a4584f0", "slot": 0 } } @@ -104,10 +104,10 @@ }, "expectException": "AssertionError", "_info": { - "hash": "0x8d97e6b6a601e10856ae70720ffad8cd392dab8169fe158054edac0bc0f0e49a", + "hash": "0x0d7dbcbd6fd7a106e16128e65e3650693380131c8864455f3a9e2c1003f710ba", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet]", - "description": "Test that invalid signatures are properly rejected during verification.\n\nScenario\n--------\n- Single block at slot 1\n- Proposer attestation has an invalid signature\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation is rejected\n\nWhy This Matters\n----------------\nThis test verifies the negative case:\n- Signature verification actually validates cryptographic correctness\n not just structural correctness.\n- Invalid signatures are caught, not silently accepted", + "description": "Test that invalid signatures are properly rejected during verification.\n\n Scenario\n --------\n - Single block at slot 1\n - Proposer attestation has an invalid signature\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation is rejected\n\n Why This Matters\n ----------------\n This test verifies the negative case:\n - Signature verification actually validates cryptographic correctness\n not just structural correctness.\n - Invalid signatures are caught, not silently accepted", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json new file mode 100644 index 0000000..6c390f5 --- /dev/null +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json @@ -0,0 +1,305 @@ +{ + "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet][fork_devnet-verify_signatures_test]": { + "network": "Devnet", + "leanEnv": "test", + "anchorState": { + "config": { + "genesisTime": 0 + }, + "slot": 0, + "latestBlockHeader": { + "slot": 0, + "proposerIndex": 0, + "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" + }, + "latestJustified": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "latestFinalized": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "historicalBlockHashes": { + "data": [] + }, + "justifiedSlots": { + "data": [] + }, + "validators": { + "data": [ + { + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "index": 0 + }, + { + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "index": 1 + }, + { + "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "index": 2 + } + ] + }, + "justificationsRoots": { + "data": [] + }, + "justificationsValidators": { + "data": [] + } + }, + "signedBlockWithAttestation": { + "message": { + "block": { + "slot": 1, + "proposerIndex": 1, + "parentRoot": "0x2c9e1fc178a2418b1ca1f4f6b7b851dc5f0a15eeb5d0f1bd1eec6faf74a65415", + "stateRoot": "0x87f879aed90d73f7069ae51bf51f6b84a38eb4054f430d0eeafb50e0de805626", + "body": { + "attestations": { + "data": [ + { + "aggregationBits": { + "data": [ + true, + false, + true + ] + }, + "data": { + "slot": 1, + "head": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "target": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "source": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + } + } + } + ] + } + } + }, + "proposerAttestation": { + "validatorId": 1, + "data": { + "slot": 1, + "head": { + "root": "0x44bfa274408b2ab880c5446dbc2c95854576f078fcf4bf876dd3a96072c773b7", + "slot": 1 + }, + "target": { + "root": "0x44bfa274408b2ab880c5446dbc2c95854576f078fcf4bf876dd3a96072c773b7", + "slot": 1 + }, + "source": { + "root": "0x2c9e1fc178a2418b1ca1f4f6b7b851dc5f0a15eeb5d0f1bd1eec6faf74a65415", + "slot": 0 + } + } + } + }, + "signature": { + "attestationSignatures": { + "data": [ + { + "participants": { + "data": [ + true, + false, + true + ] + }, + "proofData": { + "data": "0x00" + } + } + ] + }, + "proposerSignature": { + "path": { + "siblings": { + "data": [ + { + "data": [ + 1124710164, + 363497165, + 1297563045, + 500796207, + 1985065864, + 1674334765, + 820663218, + 1236937246 + ] + }, + { + "data": [ + 717506286, + 1222874154, + 247244907, + 1935061451, + 1353554446, + 1439044083, + 1124519971, + 559690834 + ] + }, + { + "data": [ + 1358408385, + 1875364530, + 1799994181, + 1828281137, + 1706541869, + 1105434410, + 545311049, + 1911960662 + ] + }, + { + "data": [ + 950179274, + 1047614470, + 1624421018, + 1703569535, + 999754052, + 1679885826, + 1315761848, + 1898141320 + ] + }, + { + "data": [ + 1740399675, + 1073633998, + 611042660, + 615002255, + 1235730188, + 87027280, + 86895955, + 887601845 + ] + }, + { + "data": [ + 136791005, + 222208790, + 211255601, + 658910733, + 1398974952, + 1962287981, + 1008967852, + 1459434081 + ] + }, + { + "data": [ + 2058530742, + 1528131062, + 586506231, + 73007440, + 119264587, + 1734418534, + 763208946, + 11249856 + ] + }, + { + "data": [ + 1043531032, + 1880288654, + 2578972, + 266901381, + 656140631, + 1917379094, + 2056426869, + 705827472 + ] + } + ] + } + }, + "rho": { + "data": [ + 903205276, + 1258163451, + 499838896, + 838051028, + 1416916047, + 1156976355, + 1468982894 + ] + }, + "hashes": { + "data": [ + { + "data": [ + 408448502, + 2097771261, + 1568864942, + 1711875409, + 932163598, + 1411104456, + 555220707, + 1889201513 + ] + }, + { + "data": [ + 1844609483, + 897825857, + 972574515, + 1126889766, + 1803958599, + 1181767797, + 685933363, + 1742010269 + ] + }, + { + "data": [ + 1849815147, + 68889604, + 429648865, + 451003460, + 779010014, + 1971790217, + 653377049, + 1596905928 + ] + }, + { + "data": [ + 2094660960, + 751870991, + 1524987780, + 436778235, + 1066607152, + 2047885417, + 142725384, + 2117521627 + ] + } + ] + } + } + } + }, + "_info": { + "hash": "0x2c37ce0a085724b8413d48661004f9e8deae40f786274ff59c6b05fc80a1d61d", + "comment": "`leanSpec` generated test", + "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet]", + "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - 3 validators in the genesis state\n - 2 additional attestations from validators 0 and 2 (in addition to proposer)\n - Verifies that all signatures are generated correctly\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n 2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This test verifies multi-validator signature scenarios:\n - Multiple XMSS keys are generated for different validators\n - Attestations from non-proposer validators are correctly verified\n - Signature aggregation works with multiple attestations (signature positions are correct)", + "fixtureFormat": "verify_signatures_test" + } + } +} \ No newline at end of file diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json new file mode 100644 index 0000000..f86d8a7 --- /dev/null +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json @@ -0,0 +1,263 @@ +{ + "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet][fork_devnet-verify_signatures_test]": { + "network": "Devnet", + "leanEnv": "test", + "anchorState": { + "config": { + "genesisTime": 0 + }, + "slot": 0, + "latestBlockHeader": { + "slot": 0, + "proposerIndex": 0, + "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" + }, + "latestJustified": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "latestFinalized": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "historicalBlockHashes": { + "data": [] + }, + "justifiedSlots": { + "data": [] + }, + "validators": { + "data": [ + { + "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "index": 0 + }, + { + "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "index": 1 + } + ] + }, + "justificationsRoots": { + "data": [] + }, + "justificationsValidators": { + "data": [] + } + }, + "signedBlockWithAttestation": { + "message": { + "block": { + "slot": 1, + "proposerIndex": 1, + "parentRoot": "0x67c920f466c1d4d6f9674f33d7c937bef725acdb07ff9024de863b2f32bddc68", + "stateRoot": "0x767763ba4284e0fb9379e182c90d1687787e83b2f4e0c62407249ea3a104b27a", + "body": { + "attestations": { + "data": [] + } + } + }, + "proposerAttestation": { + "validatorId": 1, + "data": { + "slot": 1, + "head": { + "root": "0x012023705f5384f54c111656e1d08bab57f61ff3814c83661227c4886fdbc7a1", + "slot": 1 + }, + "target": { + "root": "0x012023705f5384f54c111656e1d08bab57f61ff3814c83661227c4886fdbc7a1", + "slot": 1 + }, + "source": { + "root": "0x67c920f466c1d4d6f9674f33d7c937bef725acdb07ff9024de863b2f32bddc68", + "slot": 0 + } + } + } + }, + "signature": { + "attestationSignatures": { + "data": [] + }, + "proposerSignature": { + "path": { + "siblings": { + "data": [ + { + "data": [ + 1124710164, + 363497165, + 1297563045, + 500796207, + 1985065864, + 1674334765, + 820663218, + 1236937246 + ] + }, + { + "data": [ + 717506286, + 1222874154, + 247244907, + 1935061451, + 1353554446, + 1439044083, + 1124519971, + 559690834 + ] + }, + { + "data": [ + 1358408385, + 1875364530, + 1799994181, + 1828281137, + 1706541869, + 1105434410, + 545311049, + 1911960662 + ] + }, + { + "data": [ + 950179274, + 1047614470, + 1624421018, + 1703569535, + 999754052, + 1679885826, + 1315761848, + 1898141320 + ] + }, + { + "data": [ + 1740399675, + 1073633998, + 611042660, + 615002255, + 1235730188, + 87027280, + 86895955, + 887601845 + ] + }, + { + "data": [ + 136791005, + 222208790, + 211255601, + 658910733, + 1398974952, + 1962287981, + 1008967852, + 1459434081 + ] + }, + { + "data": [ + 2058530742, + 1528131062, + 586506231, + 73007440, + 119264587, + 1734418534, + 763208946, + 11249856 + ] + }, + { + "data": [ + 1043531032, + 1880288654, + 2578972, + 266901381, + 656140631, + 1917379094, + 2056426869, + 705827472 + ] + } + ] + } + }, + "rho": { + "data": [ + 644208543, + 218416544, + 529253169, + 1007502172, + 403215989, + 26113621, + 822584767 + ] + }, + "hashes": { + "data": [ + { + "data": [ + 373907450, + 1905897425, + 398616161, + 118479090, + 628416889, + 558057520, + 531733487, + 764291998 + ] + }, + { + "data": [ + 1055073502, + 938359962, + 1750550172, + 392088656, + 1252599961, + 1636709997, + 568483147, + 1846461560 + ] + }, + { + "data": [ + 654587533, + 2002784255, + 1460976588, + 1115305527, + 961242847, + 746080331, + 1518778449, + 931391832 + ] + }, + { + "data": [ + 1409671451, + 1790541359, + 440247838, + 1460276156, + 1516748941, + 1343472744, + 331072020, + 682552546 + ] + } + ] + } + } + } + }, + "_info": { + "hash": "0x94f9c136e620fd57a2be24e462bbb6d02ec31d8ea3b1d8141b09fd500c881b01", + "comment": "`leanSpec` generated test", + "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet]", + "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This is the most basic signature generation test. It verifies:\n - XMSS key generation works\n - Signature aggregation includes proposer signature", + "fixtureFormat": "verify_signatures_test" + } + } +} \ No newline at end of file diff --git a/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_mixed_valid_invalid_signatures.json b/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_mixed_valid_invalid_signatures.json deleted file mode 100644 index c6a10f9..0000000 --- a/lean_client/tests/test_vectors/test_verify_signatures/test_invalid_signatures/test_mixed_valid_invalid_signatures.json +++ /dev/null @@ -1,491 +0,0 @@ -{ - "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_mixed_valid_invalid_signatures[fork_Devnet][fork_Devnet-verify_signatures_test]": { - "network": "Devnet", - "anchorState": { - "config": { - "genesisTime": 0 - }, - "slot": 0, - "latestBlockHeader": { - "slot": 0, - "proposerIndex": 0, - "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" - }, - "latestJustified": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "latestFinalized": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "historicalBlockHashes": { - "data": [] - }, - "justifiedSlots": { - "data": [] - }, - "validators": { - "data": [ - { - "pubkey": "0xb40ec9783b56572d99965130ad1cec4f066df43a0fd89d22d420535c62b52711cdaf7f5f696c6b1e4df66a6bab48bb51abcdd77e", - "index": 0 - }, - { - "pubkey": "0xc5ed4b6fa46e83281e29201b878ad52b4a9b8a2f3c9d8017cfd9b81e8097e1372a097e62be37194c39e2f36f76e8906d2e818238", - "index": 1 - }, - { - "pubkey": "0x0cc8a378cdeb1a3c4b87156bcba3e87d31fb8c5f1c3ce74b4370001086b8bd5a502ea12d0878536b19cb6769b7285e1c0504fb3e", - "index": 2 - } - ] - }, - "justificationsRoots": { - "data": [] - }, - "justificationsValidators": { - "data": [] - } - }, - "signedBlockWithAttestation": { - "message": { - "block": { - "slot": 1, - "proposerIndex": 1, - "parentRoot": "0x9e3b89451933da29e3697c588770a4d63c900e0a8af56e2a4e0777abdd355450", - "stateRoot": "0x85222dc92460f8b51fe414fb34ef9f35247653eb6036b1268261a91ba617cda8", - "body": { - "attestations": { - "data": [ - { - "validatorId": 0, - "data": { - "slot": 1, - "head": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "target": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "source": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - } - } - }, - { - "validatorId": 2, - "data": { - "slot": 1, - "head": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "target": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "source": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - } - } - } - ] - } - } - }, - "proposerAttestation": { - "validatorId": 1, - "data": { - "slot": 1, - "head": { - "root": "0xde28efe59b19913717bb76a32611fe839ad69a4b3c0915d79c8304b35b57ddf7", - "slot": 1 - }, - "target": { - "root": "0xde28efe59b19913717bb76a32611fe839ad69a4b3c0915d79c8304b35b57ddf7", - "slot": 1 - }, - "source": { - "root": "0x9e3b89451933da29e3697c588770a4d63c900e0a8af56e2a4e0777abdd355450", - "slot": 0 - } - } - } - }, - "signature": { - "data": [ - { - "path": { - "siblings": { - "data": [ - { - "data": [ - 1715018400, - 48710923, - 1748245036, - 163403131, - 924640484, - 1566519705, - 1860210712, - 1236746232 - ] - }, - { - "data": [ - 318044943, - 399009750, - 1038257959, - 729848679, - 1449298444, - 436326364, - 977163460, - 1497861895 - ] - }, - { - "data": [ - 1468833114, - 1734637349, - 1929839981, - 1267639175, - 796117685, - 47500478, - 1956344905, - 1320986094 - ] - }, - { - "data": [ - 1266260145, - 725202725, - 218929017, - 126358625, - 921715766, - 1979527002, - 1695252564, - 1220353106 - ] - }, - { - "data": [ - 298307958, - 2042817198, - 1699263182, - 1453266496, - 1023068754, - 224889272, - 2049483392, - 1399154486 - ] - }, - { - "data": [ - 1430973325, - 1579483336, - 1154958176, - 318946268, - 1584562777, - 1947187050, - 886182999, - 1154818886 - ] - }, - { - "data": [ - 1056622773, - 601147086, - 1222204938, - 264848405, - 1363314459, - 109131915, - 517301456, - 938514082 - ] - }, - { - "data": [ - 483358618, - 57732057, - 329296853, - 1352276692, - 88248225, - 1800662461, - 2098999624, - 2064134886 - ] - } - ] - } - }, - "rho": { - "data": [ - 1133244814, - 826948562, - 694211053, - 360187930, - 342494093, - 526958919, - 549074822 - ] - }, - "hashes": { - "data": [ - { - "data": [ - 780469602, - 1390643088, - 2027017214, - 1431163446, - 1529907007, - 365863100, - 1195111668, - 1880854250 - ] - }, - { - "data": [ - 1605769376, - 741812234, - 1163184354, - 1147446555, - 871882010, - 948907942, - 551347671, - 840722750 - ] - }, - { - "data": [ - 1181134299, - 1236421381, - 185118722, - 573142269, - 160921481, - 1510683126, - 294606954, - 1927123925 - ] - }, - { - "data": [ - 1347741188, - 1460449909, - 596275218, - 1289700342, - 1411024602, - 1833568587, - 1711725928, - 6783578 - ] - } - ] - } - }, - { - "path": { - "siblings": { - "data": [] - } - }, - "rho": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - }, - "hashes": { - "data": [] - } - }, - { - "path": { - "siblings": { - "data": [ - { - "data": [ - 1331122823, - 1643080471, - 525470619, - 2031212805, - 2098748101, - 1243499988, - 4104831, - 2063254342 - ] - }, - { - "data": [ - 744813374, - 813288082, - 613270960, - 679006507, - 533419743, - 993872253, - 286847881, - 1451300746 - ] - }, - { - "data": [ - 11079834, - 1639687748, - 1929217599, - 202276490, - 40122498, - 1566411931, - 675580467, - 904231754 - ] - }, - { - "data": [ - 2020782635, - 1574539412, - 1138761573, - 422111916, - 1389054530, - 1510501223, - 197381584, - 402775535 - ] - }, - { - "data": [ - 264438681, - 1087279288, - 263416095, - 1860660250, - 1429146526, - 2094709316, - 867692041, - 1662763959 - ] - }, - { - "data": [ - 544815286, - 311733778, - 799237190, - 2102805408, - 1661418854, - 1157854114, - 1855929130, - 744137327 - ] - }, - { - "data": [ - 1462886854, - 1889836598, - 2029637687, - 53625032, - 673267735, - 2047374486, - 49833992, - 1921530767 - ] - }, - { - "data": [ - 110758107, - 12788259, - 1920648715, - 413804969, - 419452419, - 1819780529, - 1147494825, - 526331496 - ] - } - ] - } - }, - "rho": { - "data": [ - 754265996, - 879244860, - 1486259768, - 2046100849, - 517389142, - 1321641711, - 698992751 - ] - }, - "hashes": { - "data": [ - { - "data": [ - 1782166851, - 755483088, - 705928616, - 1054809239, - 1035991143, - 598933101, - 107624567, - 580522477 - ] - }, - { - "data": [ - 191975122, - 1845573414, - 1060661118, - 3844096, - 767890828, - 1256682430, - 1322161263, - 1960290303 - ] - }, - { - "data": [ - 1186545818, - 1781761501, - 1739225478, - 720736896, - 819060565, - 601088943, - 1836159403, - 4774455 - ] - }, - { - "data": [ - 523507152, - 640464516, - 1715695303, - 1682124987, - 1442709818, - 495961733, - 1030632883, - 2056248017 - ] - } - ] - } - } - ] - } - }, - "expectException": "AssertionError", - "_info": { - "hash": "0xf28229e6d5294df33294f6f72cb404de35083227e0f0548f9e03d55a7b8aa32d", - "comment": "`leanSpec` generated test", - "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_mixed_valid_invalid_signatures[fork_Devnet]", - "description": "Test that signature verification catches invalid signatures among valid ones.\n\n Scenario\n --------\n - Single block at slot 1\n - Proposer attestation from validator 1\n - 2 non-proposer attestations from validators 0 and 2\n - Total: 3 signatures, middle attestation (validator 2) has an invalid signature\n\n Expected Behavior\n -----------------\n 1. The SignedBlockWithAttestation is rejected due to 1 invalid signature\n\n Why This Matters\n ----------------\n This test verifies that signature verification:\n - Checks every signature individually, not just the first or last\n - Cannot be bypassed by surrounding invalid signatures with valid ones\n - Properly fails even when some signatures are valid\n - Validates all attestations in the block", - "fixtureFormat": "verify_signatures_test" - } - } -} \ No newline at end of file diff --git a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json b/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json deleted file mode 100644 index ad8df6f..0000000 --- a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json +++ /dev/null @@ -1,1313 +0,0 @@ -{ - "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet][fork_Devnet-verify_signatures_test]": { - "network": "Devnet", - "leanEnv": "prod", - "anchorState": { - "config": { - "genesisTime": 0 - }, - "slot": 0, - "latestBlockHeader": { - "slot": 0, - "proposerIndex": 0, - "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" - }, - "latestJustified": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "latestFinalized": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "historicalBlockHashes": { - "data": [] - }, - "justifiedSlots": { - "data": [] - }, - "validators": { - "data": [ - { - "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", - "index": 0 - }, - { - "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", - "index": 1 - }, - { - "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", - "index": 2 - } - ] - }, - "justificationsRoots": { - "data": [] - }, - "justificationsValidators": { - "data": [] - } - }, - "signedBlockWithAttestation": { - "message": { - "block": { - "slot": 1, - "proposerIndex": 1, - "parentRoot": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", - "stateRoot": "0x92f8e374cbfbb7a91bee6a58e0c60cb6781bc4ba4bea028be0e46e5146b9e034", - "body": { - "attestations": { - "data": [ - { - "aggregationBits": { - "data": [ - true, - false, - true - ] - }, - "data": { - "slot": 1, - "head": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "target": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "source": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - } - } - } - ] - } - } - }, - "proposerAttestation": { - "validatorId": 1, - "data": { - "slot": 1, - "head": { - "root": "0xc18aff973b62478f234db568edc4811cd2a59885fa7f712e593b9956dde7fa5a", - "slot": 1 - }, - "target": { - "root": "0xc18aff973b62478f234db568edc4811cd2a59885fa7f712e593b9956dde7fa5a", - "slot": 1 - }, - "source": { - "root": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", - "slot": 0 - } - } - } - }, - "signature": { - "attestationSignatures": { - "data": [ - { - "participants": { - "data": [ - true, - false, - true - ] - }, - "proofData": { - "data": "0x00" - } - } - ] - }, - "proposerSignature": { - "path": { - "siblings": { - "data": [ - { - "data": [ - 1291233891, - 901611691, - 1515447763, - 518859210, - 691456689, - 1723616255, - 1740655893, - 1397350123 - ] - }, - { - "data": [ - 1456518562, - 484811598, - 2010092021, - 282492124, - 1770180907, - 1769233778, - 494579282, - 1699827616 - ] - }, - { - "data": [ - 704838371, - 2081776305, - 559183133, - 1733361779, - 263725425, - 344472513, - 2086779080, - 1240527530 - ] - }, - { - "data": [ - 1039415328, - 308461932, - 2043838459, - 611597666, - 1580071319, - 1516124162, - 698977291, - 1585930624 - ] - }, - { - "data": [ - 250662127, - 1008190943, - 983708486, - 1247986374, - 1886580775, - 1647509743, - 1550488627, - 1260597451 - ] - }, - { - "data": [ - 416883050, - 188953242, - 1182024076, - 59202244, - 1978179518, - 1739410190, - 679526947, - 861617775 - ] - }, - { - "data": [ - 1378484229, - 241452936, - 163273125, - 436861107, - 388667496, - 1797577532, - 443869040, - 906552846 - ] - }, - { - "data": [ - 799696786, - 1508418299, - 1856736097, - 1058842462, - 269359710, - 1099230447, - 749521578, - 520853695 - ] - }, - { - "data": [ - 439422050, - 1286915872, - 1358195170, - 840970194, - 1786302274, - 893515818, - 370457729, - 1427474800 - ] - }, - { - "data": [ - 2007504140, - 418866321, - 198684106, - 1996996870, - 1261455552, - 2118739919, - 56436890, - 1806594952 - ] - }, - { - "data": [ - 128324476, - 61519552, - 113352202, - 1839954511, - 2112962791, - 1831734346, - 1400107873, - 849776052 - ] - }, - { - "data": [ - 694706906, - 2057189854, - 1419202856, - 2020454400, - 1916412209, - 304600413, - 1119212112, - 988476852 - ] - }, - { - "data": [ - 1607491401, - 916185160, - 150839959, - 1915584536, - 1192630676, - 166752571, - 44618577, - 1961530862 - ] - }, - { - "data": [ - 1949287225, - 1766709295, - 928506169, - 833212136, - 35771750, - 71835570, - 1852857681, - 1205452729 - ] - }, - { - "data": [ - 1644443152, - 1520132256, - 1370044265, - 851862297, - 261020286, - 1001533477, - 571576626, - 907308311 - ] - }, - { - "data": [ - 1557846841, - 210200770, - 685212717, - 1586976910, - 463743886, - 395493034, - 1562290362, - 1016157604 - ] - }, - { - "data": [ - 208831676, - 1180089898, - 2064964824, - 1411007716, - 1673605982, - 1643551528, - 1539845891, - 704493341 - ] - }, - { - "data": [ - 895079925, - 877096130, - 2081347331, - 124656629, - 1179296144, - 1491205760, - 356412314, - 926452265 - ] - }, - { - "data": [ - 1422286144, - 984088526, - 1135304910, - 162305405, - 1064769342, - 1110991338, - 104215457, - 1422827345 - ] - }, - { - "data": [ - 912789203, - 2010420420, - 429286304, - 295855493, - 2084240709, - 1193367228, - 1021205972, - 560375846 - ] - }, - { - "data": [ - 489627247, - 396093595, - 475714912, - 573904495, - 382358549, - 668148792, - 1416579671, - 1444313453 - ] - }, - { - "data": [ - 1345967333, - 723445075, - 2048740831, - 153155071, - 10838758, - 1236457738, - 18985351, - 1138484833 - ] - }, - { - "data": [ - 993666071, - 473860152, - 482974488, - 1244638895, - 1287107597, - 1492708811, - 1976127099, - 653628523 - ] - }, - { - "data": [ - 791701066, - 885184677, - 106955182, - 1572481724, - 1456627534, - 1452427937, - 490533016, - 991293345 - ] - }, - { - "data": [ - 578639661, - 1631171923, - 268843612, - 1788996364, - 1746080381, - 1046103170, - 455298193, - 562115509 - ] - }, - { - "data": [ - 235948816, - 1018300141, - 1002498336, - 201831066, - 1124789148, - 1994905284, - 561014981, - 1257286951 - ] - }, - { - "data": [ - 1045385620, - 1058192257, - 938515492, - 573527403, - 989948080, - 1342850602, - 1832637791, - 358929324 - ] - }, - { - "data": [ - 1902152809, - 252599905, - 623219565, - 758434560, - 640896011, - 207032991, - 835792870, - 1665795896 - ] - }, - { - "data": [ - 723715685, - 587576367, - 853971724, - 1144944495, - 873175376, - 498689849, - 43297292, - 819091873 - ] - }, - { - "data": [ - 280016656, - 437742428, - 255947140, - 349343920, - 1615039346, - 1488983802, - 1389523623, - 1912297556 - ] - }, - { - "data": [ - 901881368, - 1526942608, - 852049680, - 731288118, - 661508501, - 2010590000, - 868332352, - 1726397935 - ] - }, - { - "data": [ - 1349685696, - 2130099561, - 1462674991, - 1479393751, - 1013840091, - 1746802826, - 422993280, - 544780634 - ] - } - ] - } - }, - "rho": { - "data": [ - 1708063334, - 500631902, - 912463888, - 1859022035, - 2093407176, - 589622141, - 421358791 - ] - }, - "hashes": { - "data": [ - { - "data": [ - 1258012936, - 1759805387, - 1282523131, - 1087625667, - 2066885140, - 347413941, - 912179848, - 1182795675 - ] - }, - { - "data": [ - 2120621302, - 1146272237, - 220452956, - 1654122849, - 667076721, - 647293161, - 1473822684, - 610014272 - ] - }, - { - "data": [ - 588127966, - 1623774910, - 1981302286, - 1654815645, - 1739263748, - 476540846, - 1430988502, - 371554021 - ] - }, - { - "data": [ - 147575978, - 584959873, - 482088843, - 547413274, - 878230111, - 947438052, - 2065600800, - 1725116311 - ] - }, - { - "data": [ - 782401603, - 181111304, - 74795908, - 1495556562, - 2014424927, - 2103029287, - 626628827, - 915290649 - ] - }, - { - "data": [ - 1386657693, - 226764475, - 170886560, - 1391287227, - 241686273, - 1439085926, - 1299696477, - 224457038 - ] - }, - { - "data": [ - 156077062, - 984761927, - 636114116, - 2128285193, - 804007702, - 145764472, - 1829941080, - 897763155 - ] - }, - { - "data": [ - 832369921, - 238965180, - 160945039, - 1145687264, - 1389349131, - 1691977522, - 979195797, - 524772744 - ] - }, - { - "data": [ - 1305584230, - 1558936225, - 439307137, - 1771754638, - 875067293, - 1165888195, - 1802707435, - 1814799779 - ] - }, - { - "data": [ - 1584322090, - 1505637739, - 1431439512, - 2049043333, - 2031336532, - 1994220632, - 535798250, - 1003107923 - ] - }, - { - "data": [ - 536723909, - 1389453682, - 407278690, - 1778319839, - 1777727737, - 1948491210, - 1489215087, - 94425788 - ] - }, - { - "data": [ - 1939722636, - 1550150715, - 601277655, - 1185348003, - 205771634, - 1131394685, - 1434984545, - 971649311 - ] - }, - { - "data": [ - 1895618555, - 1937165542, - 1924755874, - 1626462217, - 1247425034, - 1370792545, - 10993310, - 259827571 - ] - }, - { - "data": [ - 960895278, - 1441935738, - 564122556, - 476582278, - 1152905344, - 187751495, - 1256558054, - 1184773204 - ] - }, - { - "data": [ - 1142655319, - 976140656, - 1227333160, - 2129498013, - 867861598, - 1790717609, - 866871241, - 1774362003 - ] - }, - { - "data": [ - 310323031, - 1437920355, - 878272104, - 722971220, - 1015159963, - 409582600, - 512066076, - 854680398 - ] - }, - { - "data": [ - 1078600753, - 1684508279, - 316224361, - 1222713314, - 336701486, - 1165314551, - 997088307, - 1438291675 - ] - }, - { - "data": [ - 1317260158, - 1861218639, - 1224575160, - 421550610, - 765270751, - 1827053147, - 289080869, - 538182924 - ] - }, - { - "data": [ - 191317502, - 911206352, - 127176973, - 1283200174, - 122086992, - 2069722074, - 1696651747, - 1805703619 - ] - }, - { - "data": [ - 1585522895, - 580813326, - 1019407832, - 475961126, - 2007366427, - 808496979, - 1181091986, - 697679912 - ] - }, - { - "data": [ - 1512587243, - 1963077188, - 1954992331, - 1545360150, - 1178760012, - 1515958126, - 705452917, - 2114456876 - ] - }, - { - "data": [ - 2049583212, - 1094272227, - 1039456282, - 787530139, - 1640279372, - 1330559514, - 240146549, - 1313599913 - ] - }, - { - "data": [ - 1716678544, - 878739389, - 47637648, - 1124863294, - 1855735812, - 648435874, - 1372962920, - 1357760622 - ] - }, - { - "data": [ - 1700444351, - 164566502, - 397969528, - 335079975, - 293991016, - 1078783808, - 326444266, - 1217021268 - ] - }, - { - "data": [ - 545406936, - 53426137, - 424114470, - 1111280438, - 618160273, - 1802384583, - 1667812411, - 783526014 - ] - }, - { - "data": [ - 305021847, - 844199180, - 1053002307, - 573437770, - 1003609966, - 752594751, - 1962990311, - 1014845114 - ] - }, - { - "data": [ - 1726913971, - 580369471, - 1458334500, - 153335379, - 781417921, - 1461588083, - 1878087297, - 1976620351 - ] - }, - { - "data": [ - 1342240957, - 1951347581, - 927989919, - 979795407, - 446565280, - 1833560107, - 1611077456, - 1708982869 - ] - }, - { - "data": [ - 1555733412, - 183459902, - 1845209457, - 294206894, - 487746799, - 1581300684, - 1098936144, - 1463828258 - ] - }, - { - "data": [ - 1627490533, - 1198683786, - 347829445, - 868233249, - 668381542, - 1667170279, - 1843656481, - 2118868916 - ] - }, - { - "data": [ - 25335971, - 1947476635, - 1202969731, - 1324303434, - 840681315, - 1530295647, - 73829885, - 2084868034 - ] - }, - { - "data": [ - 1384538139, - 1830543638, - 1993528212, - 829245670, - 987182524, - 1984193286, - 1630629317, - 671245330 - ] - }, - { - "data": [ - 1350732214, - 1458554923, - 1967947691, - 1326432866, - 2116862031, - 1830754813, - 1993865530, - 1629953044 - ] - }, - { - "data": [ - 1146542130, - 280817620, - 386152006, - 1428960819, - 1210084215, - 452674181, - 14651754, - 888508333 - ] - }, - { - "data": [ - 1560045092, - 1296963539, - 284985770, - 1434652130, - 229612754, - 1450040209, - 1958058095, - 1037043393 - ] - }, - { - "data": [ - 2020927885, - 18361940, - 653582762, - 1686120847, - 1597265575, - 28912714, - 443462147, - 870096418 - ] - }, - { - "data": [ - 1695586982, - 3373096, - 104141097, - 1042336897, - 994168241, - 1453130775, - 511038748, - 965536893 - ] - }, - { - "data": [ - 1605236949, - 127566776, - 21238712, - 434461941, - 1022175801, - 1240317127, - 1122138289, - 1008747176 - ] - }, - { - "data": [ - 2102377138, - 1530162129, - 909575023, - 1237305669, - 511960395, - 2038778105, - 287638646, - 545475552 - ] - }, - { - "data": [ - 123969828, - 595339923, - 285763600, - 1913417170, - 1555092419, - 2103200507, - 568212685, - 726567164 - ] - }, - { - "data": [ - 811207331, - 1566057452, - 346845733, - 1405783110, - 296074182, - 686180472, - 1562194090, - 1331754094 - ] - }, - { - "data": [ - 1195458345, - 1015303239, - 1769326913, - 1798476475, - 1959426322, - 263548056, - 1086173773, - 616986172 - ] - }, - { - "data": [ - 1679743849, - 267745726, - 813229082, - 1802821399, - 1106957379, - 681723311, - 38255328, - 119212296 - ] - }, - { - "data": [ - 538262759, - 561853307, - 1220138601, - 648920532, - 96368560, - 1848614699, - 564258293, - 877652518 - ] - }, - { - "data": [ - 236889586, - 1945633712, - 888366492, - 1363228903, - 1535081845, - 1843716973, - 870982648, - 2828223 - ] - }, - { - "data": [ - 1813717520, - 157322480, - 353732586, - 1058663683, - 767198951, - 185375665, - 1574055980, - 434808322 - ] - }, - { - "data": [ - 379825016, - 1815775610, - 718153065, - 878888419, - 2004655473, - 329280888, - 1716255418, - 2005381073 - ] - }, - { - "data": [ - 1590446408, - 1173249277, - 2092549673, - 208887188, - 912239485, - 796567703, - 274938304, - 390283874 - ] - }, - { - "data": [ - 779263010, - 747574741, - 1434583711, - 1620835829, - 1551673235, - 1284998639, - 679093843, - 1406669023 - ] - }, - { - "data": [ - 1291148586, - 1081265798, - 1526996412, - 391781492, - 1711281276, - 1313014433, - 1384242624, - 623027609 - ] - }, - { - "data": [ - 953015683, - 934645423, - 1771313714, - 470438654, - 1645632988, - 1239732071, - 688694286, - 1693593789 - ] - }, - { - "data": [ - 1677902343, - 45276613, - 2103891579, - 1112027086, - 161866262, - 1434591076, - 1951598120, - 772846762 - ] - }, - { - "data": [ - 551705762, - 1871931766, - 1065697665, - 283086151, - 2053411512, - 1094840383, - 1766312832, - 256750162 - ] - }, - { - "data": [ - 429680689, - 125824827, - 1965715718, - 2057352154, - 1776082615, - 2118510694, - 176499827, - 1212838505 - ] - }, - { - "data": [ - 1556722792, - 1298468122, - 657266497, - 1348176792, - 97032780, - 432903656, - 1713460397, - 236087742 - ] - }, - { - "data": [ - 268618238, - 781464872, - 1629401850, - 2084984500, - 1651362468, - 871068115, - 1722302961, - 712459750 - ] - }, - { - "data": [ - 1361188658, - 539241318, - 1966654298, - 1775313608, - 143728868, - 1546419295, - 665328088, - 2041591993 - ] - }, - { - "data": [ - 1660687505, - 535693535, - 1188238224, - 1910372027, - 441895189, - 208597526, - 1637375248, - 1439508567 - ] - }, - { - "data": [ - 1139697001, - 555025515, - 1728548969, - 1245647761, - 1604041527, - 1752808772, - 1419902779, - 1640507729 - ] - }, - { - "data": [ - 1960240298, - 666218643, - 1280441627, - 1940051826, - 1775703419, - 598652016, - 140095253, - 829013884 - ] - }, - { - "data": [ - 303840967, - 363183783, - 2079196084, - 1077588941, - 1843884294, - 229585661, - 84469314, - 1923645935 - ] - }, - { - "data": [ - 1487940169, - 725658218, - 1422188831, - 2055497525, - 1396855667, - 456348791, - 1027525060, - 1026406513 - ] - }, - { - "data": [ - 1435497940, - 276128380, - 1036933776, - 678450869, - 1197285788, - 816650348, - 240096989, - 898816825 - ] - }, - { - "data": [ - 248028907, - 940423932, - 2017860464, - 1112538086, - 866251675, - 135676603, - 1729849157, - 73108520 - ] - } - ] - } - } - } - }, - "_info": { - "hash": "0x879f5976fdea34463877eebb31b5f5c7c966720d6a103273499e11d7dec42c05", - "comment": "`leanSpec` generated test", - "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet]", - "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- 3 validators in the genesis state\n- 2 additional attestations from validators 0 and 2 (in addition to proposer)\n- Verifies that all signatures are generated correctly\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis test verifies multi-validator signature scenarios:\n- Multiple XMSS keys are generated for different validators\n- Attestations from non-proposer validators are correctly verified\n- Signature aggregation works with multiple attestations (signature positions are correct)", - "fixtureFormat": "verify_signatures_test" - } - } -} \ No newline at end of file diff --git a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json b/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json deleted file mode 100644 index 14fd63e..0000000 --- a/lean_client/tests/test_vectors/test_verify_signatures/test_valid_signatures/test_proposer_signature.json +++ /dev/null @@ -1,1271 +0,0 @@ -{ - "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet][fork_Devnet-verify_signatures_test]": { - "network": "Devnet", - "leanEnv": "prod", - "anchorState": { - "config": { - "genesisTime": 0 - }, - "slot": 0, - "latestBlockHeader": { - "slot": 0, - "proposerIndex": 0, - "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" - }, - "latestJustified": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "latestFinalized": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "historicalBlockHashes": { - "data": [] - }, - "justifiedSlots": { - "data": [] - }, - "validators": { - "data": [ - { - "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", - "index": 0 - }, - { - "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", - "index": 1 - } - ] - }, - "justificationsRoots": { - "data": [] - }, - "justificationsValidators": { - "data": [] - } - }, - "signedBlockWithAttestation": { - "message": { - "block": { - "slot": 1, - "proposerIndex": 1, - "parentRoot": "0x25f6beaf778b5d1cad9d33b9bc39f7d2ed521cb3f03d8b086b6af8408617635a", - "stateRoot": "0xf8947796ac01c5ab946e51321a263b897421f03497964007001ca86e15ee4c8d", - "body": { - "attestations": { - "data": [] - } - } - }, - "proposerAttestation": { - "validatorId": 1, - "data": { - "slot": 1, - "head": { - "root": "0x16e2e465a92b9d884438754ce66e3b04e25bce25bd76ab182f8dba7502a4aca6", - "slot": 1 - }, - "target": { - "root": "0x16e2e465a92b9d884438754ce66e3b04e25bce25bd76ab182f8dba7502a4aca6", - "slot": 1 - }, - "source": { - "root": "0x25f6beaf778b5d1cad9d33b9bc39f7d2ed521cb3f03d8b086b6af8408617635a", - "slot": 0 - } - } - } - }, - "signature": { - "attestationSignatures": { - "data": [] - }, - "proposerSignature": { - "path": { - "siblings": { - "data": [ - { - "data": [ - 1291233891, - 901611691, - 1515447763, - 518859210, - 691456689, - 1723616255, - 1740655893, - 1397350123 - ] - }, - { - "data": [ - 1456518562, - 484811598, - 2010092021, - 282492124, - 1770180907, - 1769233778, - 494579282, - 1699827616 - ] - }, - { - "data": [ - 704838371, - 2081776305, - 559183133, - 1733361779, - 263725425, - 344472513, - 2086779080, - 1240527530 - ] - }, - { - "data": [ - 1039415328, - 308461932, - 2043838459, - 611597666, - 1580071319, - 1516124162, - 698977291, - 1585930624 - ] - }, - { - "data": [ - 250662127, - 1008190943, - 983708486, - 1247986374, - 1886580775, - 1647509743, - 1550488627, - 1260597451 - ] - }, - { - "data": [ - 416883050, - 188953242, - 1182024076, - 59202244, - 1978179518, - 1739410190, - 679526947, - 861617775 - ] - }, - { - "data": [ - 1378484229, - 241452936, - 163273125, - 436861107, - 388667496, - 1797577532, - 443869040, - 906552846 - ] - }, - { - "data": [ - 799696786, - 1508418299, - 1856736097, - 1058842462, - 269359710, - 1099230447, - 749521578, - 520853695 - ] - }, - { - "data": [ - 439422050, - 1286915872, - 1358195170, - 840970194, - 1786302274, - 893515818, - 370457729, - 1427474800 - ] - }, - { - "data": [ - 2007504140, - 418866321, - 198684106, - 1996996870, - 1261455552, - 2118739919, - 56436890, - 1806594952 - ] - }, - { - "data": [ - 128324476, - 61519552, - 113352202, - 1839954511, - 2112962791, - 1831734346, - 1400107873, - 849776052 - ] - }, - { - "data": [ - 694706906, - 2057189854, - 1419202856, - 2020454400, - 1916412209, - 304600413, - 1119212112, - 988476852 - ] - }, - { - "data": [ - 1607491401, - 916185160, - 150839959, - 1915584536, - 1192630676, - 166752571, - 44618577, - 1961530862 - ] - }, - { - "data": [ - 1949287225, - 1766709295, - 928506169, - 833212136, - 35771750, - 71835570, - 1852857681, - 1205452729 - ] - }, - { - "data": [ - 1644443152, - 1520132256, - 1370044265, - 851862297, - 261020286, - 1001533477, - 571576626, - 907308311 - ] - }, - { - "data": [ - 1557846841, - 210200770, - 685212717, - 1586976910, - 463743886, - 395493034, - 1562290362, - 1016157604 - ] - }, - { - "data": [ - 208831676, - 1180089898, - 2064964824, - 1411007716, - 1673605982, - 1643551528, - 1539845891, - 704493341 - ] - }, - { - "data": [ - 895079925, - 877096130, - 2081347331, - 124656629, - 1179296144, - 1491205760, - 356412314, - 926452265 - ] - }, - { - "data": [ - 1422286144, - 984088526, - 1135304910, - 162305405, - 1064769342, - 1110991338, - 104215457, - 1422827345 - ] - }, - { - "data": [ - 912789203, - 2010420420, - 429286304, - 295855493, - 2084240709, - 1193367228, - 1021205972, - 560375846 - ] - }, - { - "data": [ - 489627247, - 396093595, - 475714912, - 573904495, - 382358549, - 668148792, - 1416579671, - 1444313453 - ] - }, - { - "data": [ - 1345967333, - 723445075, - 2048740831, - 153155071, - 10838758, - 1236457738, - 18985351, - 1138484833 - ] - }, - { - "data": [ - 993666071, - 473860152, - 482974488, - 1244638895, - 1287107597, - 1492708811, - 1976127099, - 653628523 - ] - }, - { - "data": [ - 791701066, - 885184677, - 106955182, - 1572481724, - 1456627534, - 1452427937, - 490533016, - 991293345 - ] - }, - { - "data": [ - 578639661, - 1631171923, - 268843612, - 1788996364, - 1746080381, - 1046103170, - 455298193, - 562115509 - ] - }, - { - "data": [ - 235948816, - 1018300141, - 1002498336, - 201831066, - 1124789148, - 1994905284, - 561014981, - 1257286951 - ] - }, - { - "data": [ - 1045385620, - 1058192257, - 938515492, - 573527403, - 989948080, - 1342850602, - 1832637791, - 358929324 - ] - }, - { - "data": [ - 1902152809, - 252599905, - 623219565, - 758434560, - 640896011, - 207032991, - 835792870, - 1665795896 - ] - }, - { - "data": [ - 723715685, - 587576367, - 853971724, - 1144944495, - 873175376, - 498689849, - 43297292, - 819091873 - ] - }, - { - "data": [ - 280016656, - 437742428, - 255947140, - 349343920, - 1615039346, - 1488983802, - 1389523623, - 1912297556 - ] - }, - { - "data": [ - 901881368, - 1526942608, - 852049680, - 731288118, - 661508501, - 2010590000, - 868332352, - 1726397935 - ] - }, - { - "data": [ - 1349685696, - 2130099561, - 1462674991, - 1479393751, - 1013840091, - 1746802826, - 422993280, - 544780634 - ] - } - ] - } - }, - "rho": { - "data": [ - 716203521, - 581420617, - 1286685526, - 1194695558, - 1641401137, - 1997614743, - 496514183 - ] - }, - "hashes": { - "data": [ - { - "data": [ - 1007398320, - 1251646982, - 1612967266, - 537381478, - 93386731, - 1083280595, - 1721356609, - 1286371566 - ] - }, - { - "data": [ - 170535041, - 1162715015, - 694935546, - 306154186, - 1318628375, - 1972574425, - 1164163541, - 93198021 - ] - }, - { - "data": [ - 975961833, - 531824562, - 2035811416, - 1364843342, - 322668550, - 458809832, - 349604618, - 2092803528 - ] - }, - { - "data": [ - 654742765, - 1159278164, - 1972996313, - 523872121, - 1805821337, - 124485604, - 1193056330, - 807117735 - ] - }, - { - "data": [ - 919568569, - 2007737629, - 1957321079, - 1976407600, - 533410046, - 2111488436, - 1620339375, - 801239907 - ] - }, - { - "data": [ - 1248809976, - 857619471, - 1558705607, - 1000070635, - 536413566, - 1646494315, - 1742462035, - 361896064 - ] - }, - { - "data": [ - 1984415707, - 2120523866, - 460541868, - 1401766703, - 191855557, - 62277793, - 839423381, - 1933336793 - ] - }, - { - "data": [ - 720993176, - 565557239, - 144294658, - 1965029513, - 1320601105, - 1033475156, - 924580775, - 1891983182 - ] - }, - { - "data": [ - 2107882818, - 42805544, - 1038376944, - 1485088008, - 413502243, - 595063755, - 208296301, - 137522358 - ] - }, - { - "data": [ - 1584322090, - 1505637739, - 1431439512, - 2049043333, - 2031336532, - 1994220632, - 535798250, - 1003107923 - ] - }, - { - "data": [ - 536723909, - 1389453682, - 407278690, - 1778319839, - 1777727737, - 1948491210, - 1489215087, - 94425788 - ] - }, - { - "data": [ - 1472227604, - 777167222, - 1014016292, - 2108428626, - 1572854930, - 936945519, - 1087148360, - 239564935 - ] - }, - { - "data": [ - 1895618555, - 1937165542, - 1924755874, - 1626462217, - 1247425034, - 1370792545, - 10993310, - 259827571 - ] - }, - { - "data": [ - 1633133337, - 1030126208, - 1634656506, - 1714432182, - 923722773, - 1592896262, - 1045605719, - 1004996167 - ] - }, - { - "data": [ - 1142655319, - 976140656, - 1227333160, - 2129498013, - 867861598, - 1790717609, - 866871241, - 1774362003 - ] - }, - { - "data": [ - 310323031, - 1437920355, - 878272104, - 722971220, - 1015159963, - 409582600, - 512066076, - 854680398 - ] - }, - { - "data": [ - 562156248, - 407660686, - 123830526, - 1475625115, - 2009710949, - 611229674, - 227976023, - 1979398321 - ] - }, - { - "data": [ - 1317260158, - 1861218639, - 1224575160, - 421550610, - 765270751, - 1827053147, - 289080869, - 538182924 - ] - }, - { - "data": [ - 1373100955, - 1325801240, - 242486397, - 1594220983, - 1224055938, - 1039685355, - 1369882156, - 2105201870 - ] - }, - { - "data": [ - 882140143, - 972100438, - 1911957230, - 1132707040, - 1927731179, - 1221461913, - 7060907, - 1965761976 - ] - }, - { - "data": [ - 1512587243, - 1963077188, - 1954992331, - 1545360150, - 1178760012, - 1515958126, - 705452917, - 2114456876 - ] - }, - { - "data": [ - 2049583212, - 1094272227, - 1039456282, - 787530139, - 1640279372, - 1330559514, - 240146549, - 1313599913 - ] - }, - { - "data": [ - 1405173867, - 1816274123, - 914189354, - 1390194868, - 1875873291, - 729884524, - 1391291848, - 712390226 - ] - }, - { - "data": [ - 598163318, - 669166894, - 2095183582, - 2020485494, - 1353088122, - 288048132, - 219781410, - 2002759719 - ] - }, - { - "data": [ - 545406936, - 53426137, - 424114470, - 1111280438, - 618160273, - 1802384583, - 1667812411, - 783526014 - ] - }, - { - "data": [ - 880153551, - 1844784642, - 958326447, - 604503257, - 1004388263, - 410341879, - 1891463524, - 1293918805 - ] - }, - { - "data": [ - 501558010, - 740311244, - 1050231400, - 540493697, - 1939025889, - 865101441, - 450874438, - 556623367 - ] - }, - { - "data": [ - 14617393, - 1741137626, - 1232402672, - 1297835855, - 180473396, - 296331666, - 678243236, - 1349472775 - ] - }, - { - "data": [ - 1555733412, - 183459902, - 1845209457, - 294206894, - 487746799, - 1581300684, - 1098936144, - 1463828258 - ] - }, - { - "data": [ - 359145510, - 91313123, - 46174230, - 237526521, - 2072622391, - 1873031895, - 797080303, - 1306795272 - ] - }, - { - "data": [ - 150143807, - 1240854373, - 2107166892, - 1239041875, - 199965884, - 143519120, - 877927432, - 463537849 - ] - }, - { - "data": [ - 1279960983, - 1843209453, - 1821049971, - 1629719253, - 1678547126, - 1467417183, - 2126155977, - 29064399 - ] - }, - { - "data": [ - 2087574544, - 1055748285, - 1878614231, - 639879396, - 1324171225, - 673620585, - 1341934876, - 558856819 - ] - }, - { - "data": [ - 1584783469, - 1012855306, - 401348507, - 2091865749, - 1821226690, - 741147898, - 257249483, - 1263361762 - ] - }, - { - "data": [ - 1067822535, - 1891280943, - 813430117, - 1276106738, - 1193107083, - 1477156918, - 1539220620, - 96504038 - ] - }, - { - "data": [ - 2020927885, - 18361940, - 653582762, - 1686120847, - 1597265575, - 28912714, - 443462147, - 870096418 - ] - }, - { - "data": [ - 1393564690, - 2099610787, - 1433569094, - 1415341075, - 667347082, - 534542891, - 2086215618, - 311924734 - ] - }, - { - "data": [ - 1605236949, - 127566776, - 21238712, - 434461941, - 1022175801, - 1240317127, - 1122138289, - 1008747176 - ] - }, - { - "data": [ - 855427493, - 1207280363, - 122948393, - 1858476858, - 717680189, - 297650565, - 1852129145, - 498572861 - ] - }, - { - "data": [ - 123969828, - 595339923, - 285763600, - 1913417170, - 1555092419, - 2103200507, - 568212685, - 726567164 - ] - }, - { - "data": [ - 479007558, - 124272114, - 1475575201, - 470022555, - 470436106, - 1311385231, - 790477535, - 123444182 - ] - }, - { - "data": [ - 1851254867, - 1287818641, - 1976227070, - 2000161771, - 366470125, - 1781542280, - 130581790, - 1069901828 - ] - }, - { - "data": [ - 1246563804, - 1413964901, - 928709195, - 135099607, - 1933313698, - 942190559, - 1583299962, - 405273537 - ] - }, - { - "data": [ - 15025568, - 798031475, - 1319692199, - 626923173, - 639980038, - 1042881228, - 1087868684, - 1534522887 - ] - }, - { - "data": [ - 236889586, - 1945633712, - 888366492, - 1363228903, - 1535081845, - 1843716973, - 870982648, - 2828223 - ] - }, - { - "data": [ - 668744770, - 1762680521, - 777619164, - 842438923, - 2088233233, - 1413163967, - 2016710389, - 1505732360 - ] - }, - { - "data": [ - 428137177, - 180423701, - 422464102, - 2076710433, - 1729838960, - 535452591, - 908577683, - 35856264 - ] - }, - { - "data": [ - 1967540513, - 1519776050, - 2036007845, - 893366942, - 2089822704, - 856708567, - 52673874, - 1680933072 - ] - }, - { - "data": [ - 981208312, - 1084818190, - 677102979, - 2063847281, - 1364366814, - 1457677810, - 1899058168, - 1563619590 - ] - }, - { - "data": [ - 1291148586, - 1081265798, - 1526996412, - 391781492, - 1711281276, - 1313014433, - 1384242624, - 623027609 - ] - }, - { - "data": [ - 200567419, - 388962948, - 476521573, - 687024916, - 1833415520, - 1730904880, - 443398259, - 436157135 - ] - }, - { - "data": [ - 707230432, - 1154831848, - 281037294, - 1768624406, - 430016495, - 1598391594, - 533135163, - 1005066409 - ] - }, - { - "data": [ - 1575751610, - 754993504, - 976076502, - 28864881, - 203441435, - 1815755927, - 2032423475, - 1425030338 - ] - }, - { - "data": [ - 1562339170, - 1308262206, - 27630180, - 395740765, - 2013010851, - 1820364392, - 1927685629, - 104952625 - ] - }, - { - "data": [ - 1337781915, - 946317826, - 736367261, - 1158536580, - 1619326108, - 291263633, - 543599065, - 2111323116 - ] - }, - { - "data": [ - 252041760, - 1817414500, - 1222652907, - 729393086, - 1201147464, - 204350039, - 1423174574, - 766473914 - ] - }, - { - "data": [ - 35290624, - 543962937, - 163333824, - 329618781, - 896461446, - 346705117, - 690957194, - 1923687483 - ] - }, - { - "data": [ - 2113056063, - 1632595436, - 1471738161, - 732872543, - 1177191142, - 1142007075, - 1194993142, - 1559467243 - ] - }, - { - "data": [ - 1982272307, - 992599898, - 912060509, - 756026196, - 1317365254, - 900172012, - 1887616520, - 86671560 - ] - }, - { - "data": [ - 2046630080, - 888883591, - 84025767, - 77874323, - 1407868461, - 443546501, - 203936347, - 412038982 - ] - }, - { - "data": [ - 1135271242, - 490412616, - 1384616381, - 596851392, - 371643044, - 98437837, - 390163320, - 374739258 - ] - }, - { - "data": [ - 1465916835, - 1762678201, - 1111080241, - 460605314, - 1685336972, - 120785414, - 1657833535, - 1990363046 - ] - }, - { - "data": [ - 1622723517, - 1868134833, - 1357640922, - 2093289686, - 1317449817, - 1456398689, - 865029087, - 990587183 - ] - }, - { - "data": [ - 989042468, - 1523246160, - 349574826, - 1340646482, - 1579176982, - 725957665, - 514279194, - 572149168 - ] - } - ] - } - } - } - }, - "_info": { - "hash": "0xfd453261ae39cec510a775702a42457e8aefbf018d4e32cad082b20bede7a8ae", - "comment": "`leanSpec` generated test", - "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet]", - "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis is the most basic signature generation test. It verifies:\n- XMSS key generation works\n- Signature aggregation includes proposer signature", - "fixtureFormat": "verify_signatures_test" - } - } -} \ No newline at end of file From 8dfcce4585203019029dabd8b85ec95002147d9b Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Sat, 24 Jan 2026 10:17:54 +0200 Subject: [PATCH 13/48] updated XMSS signature verification vector tests to use correct signature format --- .../tests/test_vectors/verify_signatures.rs | 4 - ...test_proposer_and_attester_signatures.json | 1238 +++++++++++++++-- .../test_proposer_signature.json | 1234 ++++++++++++++-- 3 files changed, 2244 insertions(+), 232 deletions(-) diff --git a/lean_client/containers/tests/test_vectors/verify_signatures.rs b/lean_client/containers/tests/test_vectors/verify_signatures.rs index 6887bd6..7e52cff 100644 --- a/lean_client/containers/tests/test_vectors/verify_signatures.rs +++ b/lean_client/containers/tests/test_vectors/verify_signatures.rs @@ -4,10 +4,6 @@ //! NOTE: Without the `xmss-verify` feature, signature verification only checks //! structure (attestation count matches signature count, validator indices valid). //! Full cryptographic verification requires `--features xmss-verify`. -//! -//! IMPORTANT: There is currently a configuration mismatch between leanSpec Python -//! (HASH_LEN_FE=8, 52-byte pubkeys) and leansig Rust (HASH_LEN_FE=7, 48-byte pubkeys). -//! Until this is resolved, the xmss-verify tests will fail with "Invalid public key length". use std::path::Path; diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json index 6c390f5..427c51c 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet][fork_devnet-verify_signatures_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,15 +31,15 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 } ] @@ -56,8 +56,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0x2c9e1fc178a2418b1ca1f4f6b7b851dc5f0a15eeb5d0f1bd1eec6faf74a65415", - "stateRoot": "0x87f879aed90d73f7069ae51bf51f6b84a38eb4054f430d0eeafb50e0de805626", + "parentRoot": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", + "stateRoot": "0x92f8e374cbfbb7a91bee6a58e0c60cb6781bc4ba4bea028be0e46e5146b9e034", "body": { "attestations": { "data": [ @@ -94,15 +94,15 @@ "data": { "slot": 1, "head": { - "root": "0x44bfa274408b2ab880c5446dbc2c95854576f078fcf4bf876dd3a96072c773b7", + "root": "0xc18aff973b62478f234db568edc4811cd2a59885fa7f712e593b9956dde7fa5a", "slot": 1 }, "target": { - "root": "0x44bfa274408b2ab880c5446dbc2c95854576f078fcf4bf876dd3a96072c773b7", + "root": "0xc18aff973b62478f234db568edc4811cd2a59885fa7f712e593b9956dde7fa5a", "slot": 1 }, "source": { - "root": "0x2c9e1fc178a2418b1ca1f4f6b7b851dc5f0a15eeb5d0f1bd1eec6faf74a65415", + "root": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", "slot": 0 } } @@ -120,7 +120,7 @@ ] }, "proofData": { - "data": "0x00" + "data": "0x08000000d50004002f00010000000000be78fe416bd6fe14000000001bf7ff64dafeff25f2feff0d2e7c29022906d430ed6eb2416f07bf12d01e6e0954c83b6b95023b142293481ab8261b39f7b4657d95e2be088c3a105659d0f6565d936e2bfa9cb175ed360500609f5e633149104dc4454126d7288e34a0a26b2d992c3e5709a02c2b6e52471d7cd254412621a979ae63da120321252d4311c1033c48f368d48b1006864f12774144a52b247d71570106a30211ed6139dfd24d080cbfea4d02658629e4410655f71c3949310ab81f213d420567a4fb4362363a561ba4714bf4a9431a3cea72129d42b205449f353dc610a22a4c590f79caee0373d0769362fda2c640f0aa0e12f3c6e72fce5c6a271f073f5b66f6a73a171d913290e0ed6943dcc60c05be0a474e8eb14e0fb4c842472348050b5d862be39c4801239ce812f0ccbb08c92fe3791a655a6d349e674f4ae6273ab31f340f45e39f07e4fb8b4f2313705249a5b94d6e634a1b1b76dc710205f96c09fe7d00e655f84960bbf71eeb64ec6c0e3a985813d8343496e50042dd4a7d7501683a09e9a79a4df7c5ba3a8bc7a9573ab99f4e6e604902ab0ce82bd88af17ed23fe0788897cc263730201d6d0c473bc2a53279e2b84e01f5e84935e4e1b36dfb6e3675ab147b40858dfd25a8b766250224fe7c5abc7373e78ed160e483f938dc13dc2d7baeb26e18190136078cca2026acba3e16283542d27e337cb39c854b37baf71df51ba5497b7de67035b83709e1acc83066e68d00cb9725085799e763f892221bcc17d425de0f2e1ac0686f4541946a7768555769f097d73842047428fc18561b31acbd74dcf1761467ff01459b771873c328a52f64c95255deb1a63dd0367c4475f0001a027121008abed41470cf545a00cb19164108d959e797994531932f57b2f0c13ec68215766f31265f29bf5e3fd41e885aac47f333b043b50e6c84b74ad8c08e0cad3b346a14dd5e44407a592047b6240d028eff6749140e279ec7396ee5b4c65bdcfc283471c17a3586f1e93314270949096f2275ea7b1e50d394d0199bba2762c449702eaa848021c9df98711662bd4cfd1e563e3d677504e7f7355f824f182adc4c775fb433ab4cc6444e184c57780c9850282a01d7e07725e25f6f9fce523101adf70b1807e6733874232df0e4e15c2adaf50a06812e09e3a2476a13f5685d7715f255cbdac7641549633895ed6918a8335f03cc7e98023533155dff8e8e0cd904836bc597bd72630b8c78f7d27304ded23c119a954b43d0fa8d100af141045b8fd0670e3c546064bae1094ef45b3c0252bd26f9c55567127d3b1fe37f2a675e872e1aaab43705ab2ad4447753d175d8e8786bbefbe04dcb0c276afcb49e6f27458556d66f4b4fc5341d5fc253dc43a0d23f1a854d0c557e0c67320dd1760ba5bd797bdf8b6427d20ef163930f80059cd9c44e0645375b46d16b28233a75468fe4a40aff113402922c3809f76b296cb4c2237ef32b6b63e87333752cefbd5f9200b531da96af19ac166957d7d9e411ba47686ce574490240407e41e9c1ef41b4f2331183aab6784461c364cd61d85f7abd5259a1c0441887017d7a7ea05764a4b568683c38d3301eb1556e0d1e2078117868276d46f24c7c48a35629058e5e46baca74b90f3a7b747b3521fdb94d1d233cd85f44117700eab1fa2ffdc4e10dc649c61aabdf9839a2ae2c5d75830f56fb93057594f9203cfd1f324fb4042134875a761de7d50e14125a187d31506206e364701023cf9d11d3cda0062ea01a0336c457567195eb7d27fec76431916a53128d8e0b97e91814d3c9235d2b9be969fdc03e038b37476aad8a5534ddf9611c84bf983b7fca8073b816397c4150c25253fe8959a841b4416c1ade5393fd305e66211664a6dbc52ec3889623b4217d43e80ce45e66510e6c5c03811afe116d0c3ad00108c325105c81503f371a25b945cb4a9307e501a844094ba655658da2354e926c713f7a562185a59650ff362a2e62efac4ef07f7e18b135fe24e21fdd0a8159f8177205ac5791290a5fbcae424437afd36f92ba805639cff564ada48c32ed52ac0ad9e99528ee498f5a78bbfc49f9581e7293def375dba17022949ef24c97112e4d9ab8de25b0548c69e4107c7d1da16e4dc573d604c3f6bd53a5ab8b5f8d229c0fab0b5552d61e95280f1a73098c3bfb1539b7ee17731236421a611950371dc715753c7a00bff81777c44a967618e0e602b080862d5f390308a31f5f4a0cebbe319220b85c21b5f21f0cd5ab242d5f0f7ad5676259535ac46f664fbe557618fc595e95e50b3951f37ee15dc356b55d5771925c236321d4d66ddff7377936691b39c1bb097918faa309e932d24af26d75525c59e72d0729085c93534579c6d0f270663e3937212dfb41256ec523e9d9b87138dbf6333c867d37f0a2a9697e6e511e34545051c5f8031561a61b580d576a2c678cce148c5b2034f25f1f01a16bc00741e6e57c12765b13fa94215ecf0ae806a8a7a4651285653a3573d33f701aa6606d5b3862e33e1a09f2d27709c5ffcd019c329049b58d966b078c5815f7ea1d15fa5dab16b15d5e135b83230d5d4c8f7ef0e803375a14a85fc8113b2019162c432bb6804115bd3663c0e3931d3eba60397bf8fd5f3d01fb5621dc9c5549aeac0c6de42c75a40cd02f7ee1545eed7ab51670c4a55e757d2f4eac6e2e38bf38ba300106c8531fd56459a1fd5a73b06ba90676db2625bddaf6705b413a16c1dedb3e46de9349056b9d77f99c0832ca20fd4392ae9d0ec651cf6a722d104ab16a2705a5b769447f70997ecee1a03fb58da918a453ba44939cc307b41c1d0bc1390e46ec84d72a7a89a1070f193665304d8f27c1a22a2bde048a49fbc33302fa582f3c813f2d576da19e5606b7540444a96c19904dc41512cc1e1ec3840c21f974806af94eb20aa86c2d7c7ea9ea7db4af09746e3eb43c710861426dc0eb6cd481b74074b35d0410fa06044534ad45a9d73a467dc8fd6701dc5177ab1a4d46e392cf7d6bcaa45c1a813f1d710fef09ad47f74487a2914ac96ac16e77d4226a26521c1f857904651eb1257ad1752435c2f3ac13c64670121083f87c70e2d91b5b7b621dc048cc52ff644a5c904ddf169d303e415921d94c091be37a7632920f1c018b0da572a17d02cee5172f076f688fe4246afa55ff69f6775817faf9ee374be69b5b8deed80a1b8e192f52f7b90e8ee20057f686ea08b65b102021abce2a61e1f3504731420cc2426b5d74fffa6137c1f84c9090b478c0ae1370f426aa11a241dc466b1c85550f68db423d2edd6907d91d37df7f150cc47ede2447f8a14860c1452143235e5228c7a12a9c602c588180267dc9a94f4d7eee221c5e47703c24b8377ed9a4261fcec19b057ff7587c17bd9818e8d4b975eb9bad05c8046e24f5bcfb6353abc554a0156b4efd42d32d826d982973f2f551cb18891d647fc61ceb569d2302f7b47426ea5a3180540f0e9c48120d0292704b4b4f113b0ceb911a2e4cfa7a959d1a775057e50d230d8a7db7f4774edbcb4f204b41015d7026b02e9e67cb327879d709c3eeb126ce912422fc0ac21095db061ba9c45d2608672d77dc80546881194d2c8a533046ca9f2862642d9d471280ed67c22f3f7a95e44810691301308fa4d712aa84c42ef9246367f1e9af74821c100795f8ea00cd147f41387f0c618d74111e1904cc0030b6935b8956814a0bfe240d265e5b0473b02043ceafd6413656a0265a531d74baaf5d7328b0ce4e23dd86574e449d16489fec19f1308a0741eb905570ccc40df6bc895eafbb7f4481a3103bfe4d7629a76ff8449e42f10522e10666c3316f30dbf0140818df8111dfcf7216d76fec604f92bf0fc38d7d3cf6bff1798d5aae4610a5b77aa30fb573f052ba6f66c79b731747655524a2795f8fb55f0ef83bed26c013c57e9d829f52c969d5088c253c1a758c1b185d134d021387fd5193a30d3f1a9cea1b34d012515fce78281c911e42c39c870e04f9170afc42634c784033271a16bb56cb94eb5d5c25ef21f113036e26bd732019ce1273a91fc3694b880e2922a3f1156696487e5c9298234071ad18ff1d967b9b040a7a33d85e00d1a3a21d7d8d9322c2b6bf3cbc29e048dd3bbd0f9c99042661d030591beb6e27fd172d4a8cabc4519ec3102ae01af8164e159728501a8c0ae4be134ddc8c667e0c1aff743a1b8c54cb106e674f80ae280de27a794adbc8539d017a7491c0ea4e6730a34640c3d1546a261414a865087e5f21d70564ad5c0e861b7c7521915a28f4a1376788370f5bac760954571069731f6e41746d06f25818d1e5334b052a09d1a8603a64a8db5804c56e424c75160439f9b47df32227375d1c796e7bcf487d2b0d4f68242d872563df3b3d88e0451338598d563f56f474ec15662abca1cb39e1547646d9914d2275a2745f241aef0cb88b327e0f56d15d50de97094a09557655701a1a03c0115afea8a6174c288b5db76f816ecd1c7b718df36447d88b963134e9a6479b319a6d1b63d744d9ea6f475ed06731facfd63da21958147dac0c729265a64e318c3b49d86bd233d442fa744f1f4c5927dfae01c412ed770d552b31f00fb72b177a89729ad34b74c841ee57d3132171ab9ae9351f6a71548c91200636e3df37756ac826e564de7da04d4f75d4adbd144488bd7d18f7a6431f2544611cd12c304e836722e8a12d18028ace4c58c72c1b0d706f2f2970d40146a3d574d2b2f355af149871ccc67701db5038323dccf94f7776db5cf7ead91e0dd11c7600ab7f49d1ca0347a41f20058a2733556e936129dca75a319f694f1dc1dd983636d40a7449411b3ab19013568faa2a0cffd9cc5db6d62a4aaca91d30bd528021f4e7707b5a8a7b0d1c9667264ce71a1ece16ce54fcb57e609d15a319da48a74d0fffd857b4033a1502548c0ee1c1324ab6c91e0366e9e81999cae419dc0d2065f708db0119b62a4ce1fd18315ea8f83de9ee5078801d1d44ed868d69d2f8e47edd7ae73453b9c46e9d3bfe618fe05762d7aa5328a94fe5159b72567098f29a3c9113805b922fb51e3faecc204881a37b30c0cf2c09199564e7856d669c9a907e415b3572848ba17cc35d6b62c99ae52c4463be4d9eebe143f3ff5f6f61aaa60582f0792d2c800f47a2d9a661251de54d3ae2634dcf7eed27cfd97d5167cad77842d6bd5ff5dd9e6df71f9b692b7e7f0e2df5151066bb946c9a9ba01811a2c1388480d063e2646c5cb8cb9071a2db644ea6663727cd374e23e36e0e690ca0631088600b10b0d162563e72575a19a2b529c422d66913219a4c728de8056b26f55b28e6274bb394834f8f866e5e63b88c0dc188065b58183d1446dcb7510801621a250d456662986a4aea51be1072a14231d9eb004a92467450c0f8192df5e38378e6581c000da7c738d379ac2c78b25600be57be4447cf9f54d4131951ff93343293840430b252a04dc44d855c27dd3b58a5cd732107489e2eaf797e48f4b881029aeafe2a2b7b9108676a647efbab826dba805e786cb11d0c2eaacc22a5bd694c59d88a4761cbd76682d5074d96878959e15648115e933c6867518e61ec60c92136e6d253edeb534ff6ad0f2f0509753f8cc54f4fa872cd43407bf345e24aab2b7a7bb01d4fdc0906ea0df6120f21dd3e119a74693e69ce51e127d2462db7556aa662e07da36fc7339cb82a30d7a866769db774617710ce286cf2a2300a585d6ee058543732aeed2e83598150016ddd6cb1b2446ee338f6556593550b4a0dc90abad3756cc0d9d563dc362a397690497e6178c4730b77b3163611963efdb9722041cb5679381dab5cc7ad6d596ed0d22fef036c6081be503450f81443e5ce34235904880822106a2399bc7b4ee4895e0f830c5d7a923579452f7a7009faa3734ca6138d1722f4514e0f946949e7ad9378ec9c96739ffc7850f7a58c36357c676d3f94713f1f4ebc6480de9c609d02e61714252755d9afb220205ef96426bbe96789863701defbd119a372ca28beaefd7a8386b670c28544414f3c575f93c2d0734da3472660f4b82fd3014679b2de80072a8c0d490a890774d7d07c38b886976d5becb4286a89523a0cb2444b37c3561c52eff659b6cc0b52f17cd90f0a82d95d0856b60e40d00f2df8a32a403ad5a35ea633ba1aa07f483759bb0f09fd2b4204bcc0e0245f48c858c833597d33886e1b2ca0594f7b1cf812c9534918425ea75baccacb71e79aac3cb8365948f35fa44d2b75c130a3048e5507d8ff2ab4dad74522e986561490944aa04b3f0dcfa2925a93e1fc3fef4160044356013234fccd60e761820abeda771a8ee16d6700640251ee12af61f2242537f3299e30ac087348c79393631b347268c9316554c1daed7a72310b3f3450e460e07e9f02ae4fa70beda9f02066d8ba328ef8283ed670fe18de6dde0c215f8325e22e4e0316398156981f4b63a5085e0b8044a62c6bf0705f162568526296c87100a70665f559073f562d30037e9b7e5f8046ee55775c7a613fef366e0bda8669350c2711606f7219240d7930ad9fe6550f872a3162db384eed518e55fb6d58118e492c21cb8c2a6e806f0423d70430760b0fe37b124bfb6ca51729005d66cb1402c70b10c45f921a4733243624c86c6614ca575759568b1381942963ff2293687fdd9c155503c06f0972f17a02b4c240fe582d571fe4a75e83fa8e5719850c1e44e8687d5bf1c105cb76a824c14ce1713718f00f70560c18dd444154f84ffc66150f0367a6d47a08d1056c194f19854e6118dc77a9961d3a9cec343eef616e66ce451303a749662a2f2a020f293a6d59b002e70a3e0e9003a7794a2424cbe4710538b97b56445014504ea103ca522029b8ab254e6cd14931d936b31a6deb382e773bd70415bd3a6d10225513950ead6528f70507349f2d0342c2e70a8ec4b0237b74d8163b32344cfc8deb60cec6cc666e0e2670c03ac87dc1873c058deb64749de05813a0bba328575f8d3934ed8540cf07d70f8b636d6dca66492617376e541ef0680697aa216e3cf6b32042f2d71332bc130d20e34746e1cb13472f72d037e186ff6e874c0c1544e9dc5e934e9f366868851909c3680ba627486960857e13c4d21f6ff94a6216626e4d294403d51a79250f6f23b6f72973df1574bce17f2d2e09a95c99562a245e96374144354e7846854703b4004c2e3df14e2db944cf63f761cf20ece65426f21b8f22051b12131ae070492a7f6101523fce1b886c18494aafc53c22cd3e5d56107f1f0c51b7520778c762d231830d0d81907345ca461a497c670c5e90f7703891343c57176128074b8c0481c1314a50cb9412ab31c930ed72712bb742217d6ace4d0a7cb6f3123854103cad5c643e7f7b3412ee36506d531a1800f0a9b068f4b0471e70136d30cfc4ce3341308d0a7a81d94c807d8553a382bb6763e2681037c48b46722af44dea0aae48238f3243b4f3f21b183f53194de7430829a9a95a58beeb36bbce995c82fe7f2ecff66e27d9503a0973e6b60e47b7567e8117da42d10f1512c36a4c02ed57e5112e967d0c26844a7274adae1356fb9c2baa75a90d915d5d112adb68016d7941531e44ac5db1a1b611234e7f771fe1ef742c1db64a8eeec617913c4132ebca95608326d6367d4baf61281309155c7c15736a30ef78614a806c2e7b9e3a5b93e70f0444965fa7947e555b26d22eb587d45e8ffb92445dcf8800544be310dad4156355afbb4550b1e055fe2e1b1e83caca60dd6c641e05548e569366e30e144edc2a1ba3901989cebe501e99ab4ce746603e00ce957b167dfa7408a37b5272ef6c14ee7ead762ee8320c34f42520437e2c2cffe9ee6e7fcb16613f4a111dd871493ca711d1796d44552b4a54ca2fbe8f5453725a296eb402dd2ae617fc135e9e58749af4ae17927586297babad56a69f6d6f5b325b477f97a11ca36ee52c55f3ad6bf947fb018a26d531a8f1051cf61a811e6818a628b13961359417152025320430f8e735395464051799b66f2597b1473a3253b226fb3f594380a5247a416e0a178b414c67ac46a937f685d800713a26413b43b100c3e6356152a6fa1a0ae215628e4aba7060282374fd7e90451fb0982a3c81355199460f1de45972547355f519db2d71761c56e65bb854370f3129ad2a4efc8402fdd5533f1a04703add6c474de4ea0b6aeb322126e5fd0615ff8cb5449bce560ddb1a922ed2e49371431d013b692c6c54bc85b1730f21a51f9cbe687e4dcaf200e0980258907e563a95611e22279a57422000b27cdfc5b57c38133c306528320d4aa0213632607a17fb088335960e10207a16aa4d52a3213d56a5a43fc367b263335ff648e6ac145d9e9058732cf0315df7c6317652c5cb1d8eaf4c58a4019514d8bae80df26db55cf1ba9a10ca963c2e81da512f1c21f640d2e67c1e276d4d2de8cc1478523a0a1ef00fc26c24c35d636cd3205e639e47314e30ae677ca116022cb5030e7c02c870259fbf4a7694623e053e0a5b26a873081ba06b56e562696232786e195985623aac4fc41f98f0a11f7c915a05a3d7a9278da55329073a8d22d6ef660f1f6b96408a577c45d133905aaa2a504966a3c43e5d7c35756c38f1658498b038f104dc37a619cc700bc3884527e8187c8aa42d2bd092523128f12975625dcb5e2a5394271bab3a65405bce5c71b6d121ed71fb4ee0cb5f309f49e30eaf78251ee4c0124fab1e5e7d99bc5065c8a8b70a1af709183536de35d0d31f540153c070a6170e5839dfae011686654a4a9697384c80770357c16d106ee745057bb92a289270871a11d0a115204e5f1d6dec395f76b42130cf07907505ccba4039d0766806e7512e6815da61ca4e16157b12bf5b409c695d7ef226108ba573469d0e8079c2ea9d27833e215058612114e0d22d1784f5823a8a13387e958dad0bfe25c956235344251ff2181bd6e81b1e623687426d2fbd3813d9ca7c2e25d21300cd9e329baae2363e58471de6f1b51bd44c1c210554652a5c51d171ee705824c564a46d5b4c2c7051e2775a53eda27a99850971df9cd11da44f4e0994afbe5693905d53e05f1437f8cc8d0304f9ce696a292261652090347712fd14ac16710960e6c02c10325d7e301af330af415c28349606495b4eeb6ae610b13d40e22727e8fd06437dafbe2803d990530381f92c794236337f843e33be99201540a6a842f558b545692d287dd6750a2ec63dd10284efe16c3f23ee507b2d4569bb76be0edb43f959e12c003f3ad15136ce50ba1b55ad0217606bda2d95c949558b09f90bf3bd775daf169166191262034ff0b53f611faf7e7f2d7d1e11cc32104a22f47c9157895727df3f28e1dfc160111f0243f2efe1660dc4067cde04b63f4b87f471ad479a3d61e18c7ac337745b2e7f5e2d869599706016905e6109dc068f94e70f78b770055dccb11e163f172d32e2d02b422db07320793163d464fd38b7ee065a4e722e52ed3c294e68ccd54862a34746a79d331ad32a8a797fd559603a8f865ae48b3a5a5225f33c588fd342db686f481bae3b158a94120cc739dd1d542c762044f1270a35e3f44dabe7cc5d7275a8452882cb6cd27ccd271da1770cf36ff61271357e5bfe2aee2866bb186733c503340084ac42f68cc31a113d3e78cf0811365f8b783db7c520735bb3905fcdd0ae017596d401a75da053907fc301539ae66c14454554440354386058f04ae3508c2dbd36ea6b2a6faa32a447277c931fc121b74e0e188392473b2ec4821dfd55bc11db5f5161358eee6c6f179a21837f8f30e834964c4b69206cc3f31f4eca5be15e917837743e4e332ec8f68060a117ed14d969f7424079d5556addf158c8e23315a4f53f446e949b4535d7853d36e2fe48d37c471dd7e17d5fe79b2208bd1f895deb9ace21a2edf176674dbe659914446eab7ff73029d2931cea6aee08cd768d2e81c9da3524a2387449674c7ec8b5057ceba0691752e1152525de98334175a649cf96592cf6c16765621daf16551bc313e142b067a39fd25e78d6e263247ba106594d7f045dd26b0f32d1d94dd13e915197422f76e3817613daf5b94e5c44011df303486f103338385f886a3a49a9650c087a876553aeb5394e773727dadb0850f52895497975f965dc8e825b995d374f43da152ab1f4ca5f2a0c0c6fefe1844e5a146570b5553164a3cea55e4971864841244771e166c02b1a5d593ede7cdf495272325402e95504d263477261e3e2597690911a91b91c77ec4d8e2f6014e14474421915ad49f01b1c59805d1343516d6a9166261cf95d2e760f2b68315757500ef8e2373509c269abe029136978dd0cc579a5666a8b5159c0c7fc5b7bb86d749b10df487d69e55d3b59f52edbe8900d59fd761b52ec986569f676384b9af66d37589d3f2768d667c090253527ccea713a272555b0654421ea69c02c6e58a17e88457e78821e5018c1e9d724c537d971bea2aa215b9a2b1bf17d940e56d522260e7eff1cc4d9941858db3569550a5f17ea89d2709f53a16bc64c8c049f4b8c4347c45e1e86af99162149a941192a6609fff69b02e9fdff57b2993d10e4e508742b04e2001307913bf6fc8068f97fd247160d0d3e8c245362d937ef1a87fa9c18106a3b04a545457b2c40e83f6b571a302fb069466267373c485458387a84ac7861d8e401a7f2806113594c37a2a1d9430d48c81b005a5d23a00b617b20a64c4199a5060aa5e3ec63db185b42b71a035a1adbf80849362237714d376797a483355e6fc614390c9f188b4ea45e2c336c2cf844326439525847a53692784922fb10642582591d10005cfefac820e9aa2a52caa7ad39fabfea73781e4b724b23e57bf486cb32de8f1d24d6f8d97203d4554b76b29007ed66c80d4e3caf540f7b62414cf26a5f019d6d6b6c4b7a399b168f6443622a2716785c1d3aa6cd4b934fdc4f69c22906ac629c26f4987d7e5b89693c7491a2020d71fa5c1b9ab0138a578459c801ee345d71155b7404f7797459cb2cd061bf2f1ce224321c2cb235fe2e4d0dcc9dd14614cdb94e05e1c36a883d826997d592796377e64b8a97464befc6721282db8f6eeec5a02e9da50e20028c7801ff9d00616e60cb0cc2a2871c442675624dfa89056ca50625f8d28e564d11d030ec283426c2be9c35ee537228efcb9646baa25a07440e812b62eea619596c830b6cb3001b1864171ff4a28f2bb773cc53c8316f6dfb421d799de20523b2f51a171e3ca7713d1d082039464153cec7d57e19c0806522503d416cac9e4d946025553704ed072c602a4fb9807400fefbed7c7caf564253d082196100733bacd98963c34ed6655a5f6528787da759414c2161a60eb018faa6094e0e9504765f9a997df84c83102bff03289439887071ab847956680624bbccac3045f7b247134c1427869f007a45868d1b6c377e46fb04dd4d294d5f15c5959f35cf561c46d7bad63b4fb4202bb1f82a6996baaa62492c203a7496a003aad723141074337bcefde5759f66643001db9f358c18802a2c9c6b4bbd17bf79174765138e9ee044281fb56f38bca829f8dc591b40575623a8fe2d1ad22d8f428339bd424044f838771e0a4474ba79512f58843b2074be455b08011322bf10111065fd2e0687c8357ded884de083873ab66bd211ed741869fa5fa07bebe7bc6282fadb5bd5232602653b2e144ce9672a89167354348b0d7d825c7c0163a9da6a90ad284e1f5e8521eb993c1015065c3766264523df537f52973792166c7ed742ff98df704b6e5d38cdcf4163d71b315f8777485a3cec162f5b085970f27c1c345dff8c6a120b0a41fd176b087bc4170f08d5af373108ab03f18a373c2918c643ec4dcf35ae548f7099d26073cda36d4a7fd0465067cd0e6e288d6f05a6b0222a40915f73ef332850fcd76f2abea3297e7f055270385c994cf8011f519226a7047643e07b6a5662473d5a4e29ff36016b3037c12e953a5328936b3a33aa136133ba51c01181411d14c1bed41bfae5445e0413b0710639d6055a2e775277a1537ebb374273c190f3609383ed46d10df1068852a45ddb8afd2f64934551c808f70b50c33e5f50144f0e35076b0caef205157e939a56bc8c6366cce63057b395307825cdcf7896bec84fdbeab24c0ff1f77ab7918a3b6f137f743d456d5820e52b29c8156f427880716b2d7cf92e6785743accc9701ff3994a72a03ad556c7fad27aa374220cd5e65913b0e509094a2c1c584488f448229527744fc88372fdc2590853d1e80d992a9f730642cd00d59a6d5af8ecd95cf397ef4ebc695a24507dba67e9b6906a322ca23867b33c7ef1435308fe27c46d65b0e575725c1d01582e5318a06aca03130d016802c11c3b9eddb064eb268e4b43b0621062ca346bda0a753bd2451738c131e25eedc6fc673ea8626a08043837a93f0f104248cc00ff355b14ab2618236789d14a7fa7c5359b2d400976ceff28aa7a5c3f085fc54578d73c184d99f92d0473f935310f1528f29005296736685818173320d9412f42aab55252a9dfb74990335a7ed36b114ae542f35cfa642b0155d4080607b7b633a7cffb568e26b700afb8b660ea618d4499b6a84c57b7426d71e8cb62a10b01605f8bdf708e43fc408d91290286e9f37265ff060300f775179466b764079afd611b4edc56d644344c1ee3944f31cfcf7946cfdd76850cfd54b0c7b0620dc2b213c1815e44577f014b5ce7b20e2f85904c9c91cb3254bdb869b698095d8836521768827015a505ee78ddf1fa14d913fd33ec51300684bb836a7e37fc4468bc2c45ff439742270e246592a9fd4219802a597a5914526d888319abb9476ea833710f0f20f1408c7a80772e8eb426736b7c62be891f1e2439f857bfe31b392e8dd47b546dc943b9a4e56970126334cbdcc81396f14457938d8b0e6a54c05efcd55a6cbaca1f6ada157e6989f8db3e01ce500dd85e7f2eedb322418d4a14752de84865ff1e264d119ffb11854b022dcebc9525bd7f30000aec47560477c7172320a9306c917772b5cbd9670ada715623b6d66438872e75650a39072ec0f81faa26ac4fe403554a60b42c238a22d34400810d6747fdd61eb93bd24bd1b83660c8fa6d0d25be2c401aaef43153e459637b22185bca94884b8555b5699b600f614118027a4167aa7c9f6521236275a622fb93be0482c97466c8b4d15bce70961cc436ef4054cd7e2ff8b9f3696a058d3aa8d12b32aa0eb923f6f72773e773873df91445375e5fe565c43f7b3bd4f99d3014976d5142f4b14905b425095236e2668382711cd5a82b3694780c474b04c15e00e6367527e7a8394e710e5c872dcc61f630a548fb3f284e76ba2a0c1e6c276551b673240c39f90218142a63d2d77c3085a6da50a36c8f429659204b9c1b004b86998a5ffd6e6e7ebccc5f485300607794c3a879c3371a1b3472eb1f728ee944bee9446f741b333ac0eb5c6e52efa715d55fb8279971773f06d4565316b2830e882afc1374f75f509d45321093d62418e0079861f9942547e968b95f02e40f6dc1bd13091470691d36bfc019e840d253f21b9c57d8a3a75fbc03d94902ae5063da90c416d599f82bd4c75d462f45eb05881778286148347188b02b1e399d746fa978765888b08478f6635b062d133c64ed52270b10745c4c5d4b0823cfc07d1045591577611384244fc5b8347fcda30bf74d8712a0892a450db39a46f48ea144cded29722279137a85e6e434df1d9275bfaced26536816105c1571798f5ef720d70c333edbefd04498ed213d36a6e529684ad724f269376f46434120f81fe0443f900515970165429777d07bd4e2195e1383ca316e7bc33f91f6c109007e835ebcfcad57efe8b64e7e7f0b7946e41c53d3440b34d978de19fa49045ed1aad314205cff593ff3ef4db0c6b91abaad224634b7bc71c7257e056efc4f35cf2ccf387c6ccc5dbbbb5248588add18453c765c5e1a625fcf22ef6a14714b1187f926735e7e417bc60a6a28f780e0302d24d530215c76632cb73f0c61b82d28698392771edee01499ea0b798334bc6b35d26a49203b4c6cbbe51814eac39c75840d9370132f0d47cafe6a253b3a363b0476ed4c094f6b3dfe2d68565acf653e8817555cb65f4d37d5d58265a56299138581ef32e86a4b19fddfd514bccd1318232b9e775d57a040a07cf42b7d812505aaa9733a3c2ab42b01b1ac287fed9b15cc450e33055f18549217165d31c234423172996c267aad5934171a5ec964974079e10f344f8c39281afb942f4c8f771a468c4a5046b7fc2c8a20d92b100ff104c1ac262f8b16fe1099e5db1260571b034793fa11c137c16c35dd1a6f3fd7dd5f2b50c579292b037577c05f20cdea36154223ca393d76cd3f218c51688fd6033823d4fb6e7cf0b9265de20e2a2ccb4512a09ab64222f2974d61a3b74c2cda8b6e4b11ec122bb1583fac94b362ee8c5245d2615d04d747c66b939b611dc5a5495f2f6c226bf7a1ce338fae73513fe1a25652cfce3a78d7970584f4bd211e24873a4435e61fa6155137fd7f8c19ce3b5c6173e75a429d486d230b40f81aacff1c33c14b2046a928e52e07968c5570575a5a6fd539225a1f7009a8dbfe78fbca1076f52ce61307e1e40adbbece718c8fc6712089401d6ae95a4460ded71aae42ca73a9b5d840d3060d2d5dfb2367f348300d6113ed459862457181147674d48b7664ba71c24b8f1bfe0316ed1e4de7bad27742aae53b2fc2205000f8ec4dca2c16128999d16df7ad3a2f5408167151f72d1700923364a357543d89791e2a17f6642a84bd701b70af933fa1baa76361385b45e23c8a3d9b86ff67c1bc4f261138ea4c151b10719882f1016d808d1bf46a702a08ada7132ec6423c4639824137c22126fb90317afd592f2e2a9d8b4701ba15474cf50350842424105feac257f62b1b3135e562248fb53f3d4242a6260e9ae777a230414be4005d4631935a4175b63f0755c14c26a344894292414b469ad4510c06088963b9323e11d5c39b035d2718748cab6a76b318a034da87245b89b98d59ba3f5f69f663e0293a70244813f2af346d7513762074492d843605588d12fd181bca3c2f8923bd3c34f9596e059431107c72763fa87c56250c8188024bbb917477921843085c930612c877604d5d6e7bcb0159103635e324f400c10e3caa0427906cbe6b3b05364a93584d0222459829dc3e8d7a0f9ba5363341ae2433912e0ca8e9a41a202bd047411f857aaade81749327582abaf44803a39f3e2d8837cf7a1678190408f0c56c476a0b707e26922cc5669b5e989f7113083a0c1db56a274afe41995e67ec1d158bf94647e52f97577b5c827508d438336c5afc0dc7dc1968c6afb53b5436e16e5738be4072bfef25538de91c65e67751bbcf3a06b236b76ef1811e77b82c2f2cb2a8da5efd590602061da0083b283c281a224b30aeddde5d1920fb7e7f147f0a3a23cb46157a2f4d78c925037918871c1fccc72806a1463b4000c40b081f2a61b33b0173a5cc22392dc35f6d6274e212d1d76a6a75ca34463b853e7cb027be64cdd2721670a72022b1c02f7ba2e6ff661bbdb37b48bff7760d3dea1df1011148a0b75e2dac1ada33dbc0294579093118b0037668cd26011c45e17c45c552bb7eb65c63757962903ddfda6a11d8330f6b1987790883382c5525841077a0d0b8449ac5e833b1f3072f029b2060855da9073ff28e29fe7efd2883d6020a9dde0e65dc4424757874be4036f9c475e93c37044b740e4aa2dd8050c5680b6b6cb7181e389bb2646a28c659465bdd58607ddf07f6e11f185637142b11ecf71eca0c7333c3ebe1793a68b34af02ce160f63def1aeef82434a361b445b1facb3141e3751359a75c6ddd84bb6fb3a8f25948967e3dfc27c7680afe410440b9ec79a9dad3099f98a60d866cd075d99c3f52586bbc66146a9616c59c4351e055d21e699b944052f29a4fc01ce80490995b32d26f1b17f1603950184260656398fa46ce46fe300fe6b9665976d127a0d2ea490bf57343867eb67a3aaa243694a1045c4ae07d39ea656502ad5ea17e0f881f1a776f53026f696f25cd1d612413fb3452091adf2881c4721c37a79e543e246014a1c593676b467a27d1fbf63e5941286a11426204f6decc35eb6d7468b7a720582e80f63aa767796497b3725abd4f705f692a8d12d9edb01437e47e2e5ff7933b7b49cc661491ed6a539dce2c1eabf713d9ffb5703aa9927b705d44604cbe036c049dca07ca179c6ef9b75c05bfe4e159debe955cf0d55f104c28bb73ef34de5982ca297993c25348aee7f012af455a0cc8d89e243c16a949844b9f1187829c7eb69605190c76804a74f47b55ffc29f5f83e4583b05df04190d918a310b483051b1f89474f6124e2bece86047c7e2e86a57106c490afc4e1628aa7e76557e9436de7dc2793df2d000882fdb5f4ac559391f906535f2f4501d2916c849a5da715a5320b14fc589ea4316e7a73a2297697b5cd4d47ec85e5b4728cc56220995146d73249c0c6fd4f17b9ff5db2f96a5c209f4114262cea5567c4fbd3e7c26e30256066ab41ee477f456907719019b4311630bf97327470fc055cf7f2d4f07fedd60047f82189ef7f3530392bc484ad39b00d7ca6a5922dc276160ff300803c8ed32b0723a56b197d8761634bb1314baa97b4a992e4d21814254a668286c7814a204f83dd77d07366f7e68a4f117de4b2a4877ffa454bb4fde79cd5eed2f88f92c680e82a055aace664e9abadb7d4960e54d4f2fd368607f156469adc55949b47f7e568311200b21243846600027bf4682094cc8864c9f2a014f89e35f6500fc236cc329441668f8e81b58a3ca3c0de736414cfda81b3a15a06c353400255aef952b5c16db3d94945c3c5038b37ec74c4b5fce4c2a11925ec92e3720de0d7804863594eecc33f8bdaf09551ef94639116274af25bb404b5ea36c32b299504822fa204499756fb2f64573c6737209899153385349801bc696b011512a9f7eb2acde550be2052c07d7952f5de2ee27e7282b5d2bddf226e7c6d14cc01c763aa012c7301af44b7b2fe5a87016fb3846a63acb0fec224933fc973f73d9f5de501c9f4f264f04b44491ffbc595503212110cc570cd90e4715252e3a7bb10fff02b7e5873fc2725f2fe5a6e11605a1b05c62a66450412c2f24a630a520ef7a1c364329c210891f2d0c409ef54149839f2516894d27d909ed481e616727adab21488d7d613009ad452395763003dc575e2e0929d233456fc478c7993e5c40a6656cb57eaf3c734ea012800c37290a479319bbbbdf555ffd3876b900c32167a32829b972db465cadc927f7b7405e049566599f54a8312baf9a1ec308bd01a7d6920614330e1f3fc8cd273627532c56d3b839e3c428216d55b2479f8b2868be41c465ab09247c2aae42211ac98151a051ba39bc5a806f2ab57b2ba0e64740ef7e3f5518df2259936b7525bc8a9965eecb1448b568b8219b640664030d801c563fd748060c1c5e176c1945cb94323677f4f725e98487227ed11364e643bb113fcf3b58b7e9123ef5176264bcc1a11350fe7222a971c05ce82cc63b54ba1946935f6216687d90125917bd639e39d270d7d95e60c91e0e4cc63a5269053cf37e699d030eb803a735588cb928f7e6103f8b3a193aad934336d107894f55978665f3c9995419f9997eaee3c24e1f7ba60b74495f4b57e2cd375e93c778dff19c14d7dc0d5cf56ff86f1f7da958e570fd17e6e08651a9170879fd5f3d086f3aca1a71e26d09c20fea2e5175d945be7cd1750f04c73562d1891c3da085089177f348fdef4f542b49b55daf08ba405c63ab62d90dda770aa6b34ff8384a6ce678705c7807e40089f11030625f4463d374a74a3943836524a2d57a2f16ca7986bb8513f787341b01de115c0bc3ba5b4a08c02b1eba965f035c924e22c85a156d652f65c7cc184f32f87c6700056940b4cc0759ec12f71a4a8d6176e5192d42ad8c8e3226e6773d7a75337db26f810ad76ea61c9cb0d765315422003571813b5c60e41b5e59722f829d057110a7455b2097457dffcbb84e9c75d25cb0fdeb6d99d94c75b46a26143584c055d7dc134c0538125d4bbe796d48ddca521134720937622a052403c1350cc07319327757759a3f6609b44dba49c034f6313ab5381fc262701bc695c36285b739236a5077631e83cc56701702251213643405ffee50f3858a65ec1dc2036d2f943008d72e4f53ed9e189082f61902b76f4a3eff942c766aa5658e36586222ea1f6eb0067f7ec8c6df148f94986f2c445a5de14cfb3b98644a16468183423177e70022d2b8621aed437ea2438f4bbcef7b7e9865f9363f5b2f479fcb5672568cc9092eb7b42645a5357df23ed8068e8569301cb4042dc1c1824098ea2e79911b5f054d6a001697302112ccfc0b621e41061ef9c4e777a737453b76e38b7e43b51c7a2c576f3776fbec1511150d245565fb587d3f1368b248f10d5e4a942ce3b1c50052ec595e5984d30cb2913859f26f57435620e54a57c0d842313ee370625c3f713ab73b69ba661c3d08b7b154e28bf46476450e63fc6e0d1045b9c535f6f9f36e777cc404601a576968ad9601bfaa2765f4c7603db3d83318137313507c1896056a56051102738e3d4c573158c1b9f36539a1b97ddd70152b2f0ee978094e28755034016ec349b00fa12b295075c5616d27ae7d7b12eeac16890a1b6421ae300af20fce67c7222c298a91a76cd8b3127de847222d4a18cc1f463d12387272001ac68e725253a29354910c3b68dc54db7e414f2f12de81514dedd9c06191edbd3194607e436b631210302d0456f9a693303deaf871b43eec6175657d2d02a38c088ab9db2a5c97b001fe782668d8ace34b0bff412e3ea1187e93d58c07300a973f50cf977ba0f8fe703143c05b1f56e516a3c4f84f35a1283653360d14ee67b40d4e8ace073c6ce31fd1708a122cfead4254c1725fbae2883bcd35cb346e804b7d309f5e60c07be76948ea4b4dfbb10318a71b82486049482cad491420b10987068c538314117cb80bd87b1054962dc50bae3e7615ab67be0ef8ce1364ac27110089c0a500a7a90b066c36ff2af98dd84e2c37c545a2f79f5add0c2246bf26b064cd3c7a012e1af2184a3b944b8567c974fd089b69b89e4a002b095363cf357042e33ea5201548fc75065a15187c63f9361f27dd3a0ad61763be40d14e70b28322f340b70f21f1b56c1f48a17a134ef51e8c5b410e0aba4803d0505050b1862771731c970b04a23d71215c85185ae1830fec43061c2babd11249ad4846831f8b5d7ad4fe3b7cccd45078f8113b9c19203abef3a27ae202e87e7a6dac2feee8d26cef5a040f6de4fb6af8e4c77a834f1c0f130eef185684c3310dee061c25201410d8b5aa7d2023e067a70d434da031911b25cbb348ef2f7e10388ce424c89e64219d5bfc5e0c97163aefa8dc6d088a692951f65b5294846423ead7484075575d08e14eef782d23ff64491e0366751f5415cc2dbf4c62b72c453dd46a0a9d88707330aaa610d7e30f1690ec4247b7aa0d64a7955947e33f57253a9aec0343895e5c34210d1ce81e523b69f3ec7e845d192a7060532bd093c8053a51ef233cdd7848754505194792354e91ae6d19fef04560400bbb258bcd144ef5f767714c45d76a0269653ff6968a6ee43d8b373b2cbc7be809c164902703483107b1317b45e513c33eca7433cd7f44ff917566562e303ba20d1e55a19bfc0c90f2f66423cc531ef5f1124545481b3e5579af7128d8d807b81a5b0368408a2bbe031c5625f76b0bbb225835460762304b0418656332750ee0125f5b73974964219b6142fa9a2f453664b759b5a65275fdad904bc72534718a93ba4e40e5d400f4c3f726cb981733939c88645b4c891f7bc03743e810de0a70d51a35669643467d30d766394b8562d66c4d3b6115ae7a3800613cd24bf34d8fd8d33e75c72b231329ae50a5030c1a2fac494096534139e2339f0424e5b50ca4987015de3c0a7009e4480cb439696f287877548c0b7c7bc8105528e4292365dc553a437669347ecaf31018f1b3e4501462187786de8069bd1df33a813299126a3ae1367bf3a23bfb415c4ab6261819d86d416a9879ec020210f0051325e445013b7945154d801de32a3948ccd4685742b1ea66e4bea920624ffd42c1d00a1ec1d4a56be6544f1391bb332241dcd572fab551753285df25a2ecb519562832232b4a021f8058576506105b5827722756713b4c7799ad18274d0206748d6c481fbbe34e1db5077a08bf659d77e3dc2171ea368423d2f351451f010b524b3fee28e1aa1878efba552645a02703e3f28f226d55b4317050095833b79e26a33609380e0e911e7441c5771cf31a32a172c3533c5198376415a43c3a02b06e83147e79b7420147ff56881c00f0562edae5bf7c5d62d24183a088061cbb7d1b2a43d50644443d2eff251e097814931af5bbcb40b9305112be69b265a8b32a2fe0afc209d6cfca59989e4b3040ff9b4ed03e7e4abaeba807a729952d027bc10d1e4ea7606861ff0623e9a154517258765fcf3733e4ab4f7b60fc916b0d5d2a2c3f6a1b3e4678725979578d7e8177af2a27380b7b8c429b5829762152bc848867f57b5d4e97723a10426f971458190a24dfeedc343f029e59d50aed38a8d4855a0092d354e0660034b5d94b477a54c125d4333d163e7d3c514cbd1b5fe6f5075463a1ae4d9194d3468c34225fdcdc5c38a4f70a252b6bc00bf71a7d15c0340e5d53aba6361dd1ef6a8f242013e229711406e5227baf3562495e55f070e6cbec28db314e3915858c5c21b284791c3228390a707069629c3d66bb2a0b37e72bcf5e583b245ecc39b966ecb07e6f8d9c100227e6e7651922f7415d52893158d9990c3717443b326b891f14a2e465be1011466619cf1d1cd25e0cf6846a33d2b57567d5e10e7a594caa046b291b5539c03f75b5d81464dc04c57acc761c0f60fcad4f88d3272ea123e21f8f0c3e67751a4a0df6ce5548a34b0f63c42f686ad7ccce1754a6c67b086e075823287a1483abe811500d402fb8f6c35e56cd8a277cd8a843eb4aea2ff23317509a2079162065512a9fc2d36a2b74950ef0517b123fdacc6c78b4447dff4e8c2b955d130ad763b477eef2597cb666415a126ae73292cfcf260c6c9c58eb1fe17053b8194072eaf61d71da1c29667006078a94ee70d56ad33ff1f93a7350113833cdb5eb482acd1134e2b18e0610445002df1d8958042b21271764b4799d16f9172f4a453671a4dc6bddeef703d9c75d4f4772c16fe61c5d43168def13cbd4ce2bf9649509f726063554d68922f736447b46d0995b6fd07b4abb8a823903784e2f216f920ce496e635ddecf563899f5a45fd3dcf208e8e5664b1ff0d06ec53171eb9c39b2939fae645fa4d556131edbf6bd0ccc627df8cb324a93ed24510d5cc105c598e0daf588d0ab085cb0b2377df4fc2c6820fe25986262d8e1452a7739b2a3bbe1c768290417ce4f9b0488e0f6e02b43ce86c801a7e5b04ebdc48892f9b743470521a4acde02d629ba0714a4e105e49803a2148a1aa12ea0ba659e91d7a573e9edd0f685bac5d42cc7c60a5b4170914f9721d0f034f2e3deda42b9b75be426aa955155666e8403f5ce4010e5dbf00b9dbad34d8f18271505a194cd3312538cdc6142ead31f6470957c364a6794c499b721712c4f2a30d409ce7049ada1e296228aa7ae91e8f31094a6e73e3362274319c1567fc36540b6d229137634c3b0b38199e6d29cfde419af7904fc31bd56ff3c9061249ffb80df2f8fb4b0d8157324334de542d051364b8005a70da22450467cf88486e63271454f9901985cf1a526dcdaf6f0ef2a745000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b70244c04d4968a9e1062e32fc07631c404c65f483315f312f7525120df95db3bb077847fcb73594d3af12328bb52ff96aa354ef09c73d8882266370eabd4c4eff360e127e96075c9caf37ae321f639f22eb483d6c087d66e9613a67e9fb13113abe4e2fef6a488331d35ab939c6418138226df2dcfd552fef6a488331d35ab939c6418138226df2dcfd552fef6a488331d35ab939c6418138226df2dcfd55d4221000de1fd535a0d36f2ed49fa95aa2970851b8b12e42262b9572877a964ac8cbf0647441dd7b977c0d2c42c9386444c1783af0532530d00f892e6fe21b61e977f0401958d744dd2cd23a64e354158fdda215cb48396e88ec69261a8f5656bc919f38a3dd846b741fab309b0dff5b15fd356276b43e6722209248377c0d10beb33a1d40e8ab6891cc7e7880490523e990b23e9ac16345ec12a57a4af7253bda2b7525bda52c163eb757583d23aa37ae77d819be52f7315cca3219747b651534a24a1b7b877d11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061eda1468361db23b4110a66ef95a17a73bb79436fe21b61e977f0401958d744dd2cd23a64e354158fdda215cb48396e88ec69261a8f5656bc919f38a3dd846b741fab309b0dff5b15fd356276b43e6722209248377c0d10beb33a1d40e8ab6891cc7e7880490523e990b23e9ac16345ec12a57a4af7253b0300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000002df9c4228acea600195e57699b163e371a978c440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ca8714dc779e96a0331f51b8fc4f519ce4b4e0ff7f2083c2b23e03444900857f968071c4e13ac4b2429417eb6a6df4ac9ea9223bf45e73dc9ffe4052a0a0c3e04a8067ea474786661b85756198ca85e04343c29ca8a4b076650597e017f2946ff0dd403fd3928198254fe7eb1e20740d66e8e32c76c131ffd3928198254fe7eb1e20740d66e8e32c76c131ffd3928198254fe7eb1e20740d66e8e32c76c131fa48a785db9c4cb4a71c1cc31c4dcc76c41686c6eaadd8028300ad96932bfee47ab69df6959aa765c1cddab5bb678cc2832f0bf47f5f55e23054d2c72804be4715805f259d4186609245a924704e0285ac0f98970ad4d762aebc17e72c74225623db0693d710fe350cd3b166d65b92c5a99120b4fea9a42765246151d3c2dd3359fefbc066d65334f59710d2a0000000000000000000000000000000000000000c64751513f6230192eaf01602687415e253a0d5b571b651d2e7ac71679cc2b3ac6d50959bb715a0f4dd3f33a3418350d000fbb161ecfde48802f5f32a2c59311ef1ff26244fcff5c25cef15ac4dd9b1e25e2823748fb2a6e703e96484bf1817d618de540cd924444ce5d964c1811b26cefb1060d7ccfc37bc842ab6fae14032d764ff173a17a771f2a2bf7476d255e48e2c96d373a9428105885f94d5fe1074365c6f622a9b05d3e8027d059b6325433c402715200000000000000000000000000000000000000005df81a460a4eac2110e0c030a39dff0f11a70e1b780f595f91c44119cb19dd6ee5425204b0a3236788959b2e2d6aa84ca95be3440b1c2a4679e510257714cd7081501600419b9341ba18777e8edc1f33a9dc49010bdd3e6055de4f66df50e759f9386867411f8c120c3451629fb14174d5f3ee42bedea3348d9ace3fe95ef820cb16c7423624223a1af0c00619f9082afe9a8337a8e00a58bf1c2b7e67cb1562b2196219a414ff360e145a721f958c534dd7ce700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a52db7c23360c4f572ff26e9e12874e50ab6f1edc9957264d04646f8210f0294880c71a00ba372982e8dd769d1f1875775aeb525cd5ef159ce5db44d756bb0e5df462676d2cbd66c161cb40a37d533fe00f603e15fa364ab6fd6e0090f888398f1b3f04b5456408b428fb6fb5d2540464518048a5f05e0c8f7d3d509e55d8663867787140f10077d6ef6315232c8b5d7f7aaa63bfb25c3783681068c3393155b0ea316f1fc7dd344e7d2b2738c96f547ef338083952db0d23360c4f572ff26e9e12874e50ab6f1e3172bf14f98bd85e0ebe526d2027626844f296071d26834d7223ad00318e1b34df698f71c962934167f40a696f20e514bfbef55aa8a9067a3139dc4607e73c7aa53603336f719e40bd0afa1d0e77d90985028b425cd3014534cf2f669fb7517374cc811d35e82854e73acd79dcfa9a3e1ed1cb57272add22e76d0878a5bf98268567e0138c400476895d8620d2bf181328c3372caf1501465984bd410e0b5816f1e51b07cba9a413af9710124ef4a47713b8c312ab1d960042aae62c9d7a7b2defd4055c3ce7aa3cf7cef0743947e4159d8bc324d3f82a0bdf9c1a5d9624b42cce5e5033fa1da236ea00e756e852445b27de8b32c747ea3dd1f00157ae0f5b21ccf05e33c29f9707d2fe5f4cc943a47c8b3d7d4f7265243642e6b356f3f8c110c6d79531281b3b5b5a0be91aebffa84be2bc380e93360a7535e6891f7aac9f3cd786fc3676024f7023ff3772e2889c627ff99d3ae1f97047e3bd0146c9d662234dbb7e556289b11b8df1b630e23c7339d20dc25200879e79f2913a43c426822b90c0ea77e30b0460fc8b5f4666064532cfb8aa251dabe927fd26036205e90c5faf565e6ffbd13d4435042313a5e19b4b8894197a8757b94aa5c35603563bf03d7545936104af9131b68ae26f806f3657c99db46e30645b022d4c99329d0d560de87893411e188a2d961387624630ff1885106d0b2e0c887e8566927ccf24bd6bd675130883eff63ad1f97057e3bd0146c9d662234dbb7e556289b11b34bc8413166e794c0aa6ad1c9f58d84661aa910aac380649bae2661e5c461b4e1475ec057f09dc0f9f94432f25980560e491d04286e76e24d4787848643c27771de4b2094ff83521d48c324ab6016f27cdc617180bbd5c0cec3e692a92fece1ecba5e03537330c59ea4f025e897b0e134600ab1fb9ef42036370856ee4ec364fad7d875671596d5feb552c48bd481f251d1af82738fbf44d00e7467302ff042bc1f97067e3bd0146c9d662234dbb7e556289b11b6488cf56a14c6968b2cb63287f32f3439d9e3164d9fb86156d0f7a57d9e5b576fd9974570f6f1b288834f36313e74e7b4f95e57911aa324cb831525ede1992435f36c0736c2474148ca0473bc6ad045631ac1917e3a00f1a1b97cd21daa3a35019418d2a839a127457cc5d2fdb981c1145666921fc4a6a1e1771385fb4b9d70873d9075fb06bf924dab9b50dc0d0694717bcc6640d66e0299dc0f63dffab99440300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000008df68f69b4f4732619e73a099ac22d59b8688c2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7120c5b63745b71b3c2373d88b0314704aabe0d5ef6a755e3b58048ca59665a01cb125e256a5823033bd45d9895044916cbb60ec561bf741f740b51d01f4447079fdf1dee8bb6796cf6147de6b3a2733608bb7e7de5cb22833da368191bcd477e7a6a690be58d72e382db50a9c3f459c24383564580c27b0be58d72e382db50a9c3f459c24383564580c27b0be58d72e382db50a9c3f459c24383564580c27b0300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d00000000000000000000000000000000867fca1efa30db7395083128126c5a774e1c234a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e7315738bd10e524af90f661b47de3f42ce513e8551823a773beb3825894806c23f1f354f2489135dc0126778e12069c104584bd23e5a44410f435e2ac72d59269f5e53eacbe22848da41745a37e52840d1253d5fef0e03b1fefd279ae31311f7f1a374341054413ef44b31d2ac132dd1b4a11f1e95bb6b341054413ef44b31d2ac132dd1b4a11f1e95bb6b341054413ef44b31d2ac132dd1b4a11f1e95bb6b1700c478fd7b9707d5e5451432b48850c1d22829e6acc7630b731c525d4e5d100796da33af293f7680c04d32e4cfb93ecfbabd08d98e0d607cfd485d441f167237cbf043702c4d4ec7922b28d167c370768e277da9f20b035a33175c03eec81084ffea7541f3a61201ed4e57386e790c07eb710140800c4c3333900c9fadc644046a2956586a6c217045136108218704aa908d10ce87665d7b3e816e33279220af637c46fe8c852c0624c57d5a1e21697885bf240cc3964c2b4eac0502b24c435e4d0021fb62283c058e06427313fa52d23f3f6e8c4af629b44d1f0569c14a474c144e080352f04d34a04e3d7103166ac78bc3304f4c581f3e11d2359b76fc4133521614d2caca5796d4704a1cdc083efef3df62997dd1484e249318717da2572be396412f316d7e003381054d059903a048fc4403d8a21e75bd876e16b340526ef0c33b3f8e88055f2c71452850333c9fa23e57667ff74adeb8c60b7189dc5863c8702f40fd3f3daead16364e8a673e8a5adb5a696d64677b79e85a17e2f32835b80218da8283249824f31bb014303d1542394f5aa1e9280c76127dcafb84350e7cc17964fc947eef63627355a8b52c47954468db90b20b646ef74a33249e2b3e801b7ba18c622fa1867738de116f36c1cfe51ef83df70015677c5a6142e825abd5d41ddba4e573c75736511a28d5024452f25836e26539fc8dd378ace82b20b25d582b4c921f607c2a29431ee606518681a559cd6cea1ace382527cb93344f463fb22b10db9802f6c071786ca9a64566229e0375783b4532905a470d34bf0199d1ed32bf91cf2054ee7a21b313923197b10b5e9ec1aa3a35f0e8761a129834fb1e6907f3e2aa60bc1a280a847c657bae51915e7814121b893d2247ccefbf42e9d76750b055cb1d4641e946a604ac73db87d3356c49fa6aba0a503e3c017b3d0ed024561f4f001fe6fe9424710e943ae5be0c26bf4c482d928dd760d7e62c2834e440662431c92b8ad8e811699de2690afb606a1915116d7330725b9c80da7019910e433851c51eb721a24ac6e11f4bbc54322a748144027f5ec45d3099cc5bd148f73d1c8492542fecd75375713e467e3e433b24230e742231ae239137ef7a62d57e67cd71d61090dae3384333212b3f741a6cfdcc117625623d58e8e8812dc8fb633f2a05ec1d802a0d16fe5cff58426c7f3b04595e68f17cb608f471a3361ac690325fc1512b303c833f5f85d56679095b23fc304b1dd4523756d6aea12d7bb36e224cc80e37afa5b43aab9c742ce00ad41aa7384e641de1e179935910413e54482c5182a86b4969286df08ee1287c8dc26f016a8c1094f3d45ab3421710154970757b71a2431c2a7a56d2a5741d97cd391f612dc77cc0612f61fa55756b676c1b5690ad5c101d43c427ff3f6061a83eea27a616b92a270c2331f59b487ef7f25f407c2fca51c82a7827424c6f000d566f44495d7b0ec766bd2f4017c76596b98d762cefbd6eaea7f03ced0a1e684427f07826d2bb658c9e8d37d6c99d40b766ae479ac52235ba79807d18a3005d9e2c830e83f68d3c90bbc82b5f8887147cd7d465a69159617fbfcb0b47b9d839c0e93625ba671646b1c26f6bd9787006f9e5b9598dc31a5eeca57671a2e8c12f1a2e9e753eba01467ec0b1687a3cfd5dc9102644859457525b578204cbb34e25eff92b53a874c0001c565361c57cef59a002c62e3048f1653edf3c2f0ba57072510a3d76b3bf9a168449841fa891171a4a5bca6194c826038f836544fff08d641369da6b940a777939b83e3f60d8df39991c013cbd6ff12707e831217c8ec8134f33e809709d6e77b196451c66443101d5f71c46da2e9524f92e4e4c4b122f34f621ba272362f507dd30a7213ceb14566c664e435462410841d4db2e7ef0056f1ae27c3bfcbe2e006f104975e69fe328e782dd74ac5adf1b88991c3ed1c1d46c420e941db21e25260263b16b37530d6d05a18956af7ba656b85c0c37c0e2995614a10e7611d2da211f5e975ef5dda568131e9d0714268a1d5a3c6e5a14fcec5eee63ed374a3c357001d90848b58073666f88822be98574119f51ba25858b397353a50177ceea04206346154fbfa28c01943a9e1585b06273110f9150b962f01c43af25518caed6404b985430bbe524486404f770436f793cb2ad620013f03764185e960f1c2f5805ae44de073c12386d19463d50223c14338d3381417665192c80cf9179c0cf6e372169672059511346cb4fd93f1b31222a3b698e6354956518776548615196ef5629f92922e43b5a214938c264367c1932cf3af57c2407342fc06f1f31df249403d536456f4bba7b26e186d258ee372730c5632e48b60c6f4c2facbe12a9c3943b56410c002799b93ec4ddd806e499f92cd22c6c570e9dff374b3d017a9140a62a2f16084ad88f943b28ba6f0d3a459b6f094e6a1d19431c46cc02104a35ee1f0544ba101588da4679aaee600c4844d46dcd144d634617a57a70cce730847b363fc4184530a760e92a824b6000d1dcdf3c5c468045edb61544199e4e407c0ba01d9f9fc474865c1725245ee20e0501a766312d4c70c7f7cb7e10e38d018e5e0621e05df95502d8e35254ecc54a0131361322dc5e3f8c4678628b67b41c427b8a62dc9d53377a1b1e639fb5d67b1d7a7605c9e19002d7dbd2195b554f221ea9b22307f95d4473c19d4781824157e68e8e0334e509582fd7f50a49f6ea1d5d75ab644d65d842a916d1565052b60b583d1d3a07db6647d21f7e092f807815bb157971958c000e7e9e2958dd36b904d470081c2059e826dae7da4abe74d03786c29049ae9dac3cc2ee796e3c8d1e693873e7534c46c61a92169a3785aac05ee3f1d14f37537528a4216c374536ea36b4ddc340f533bf41b81aec2f53be7c4837bb7f4e4805170cf3730f6542be7e166a163e02ccf2ca42d3493f75622aa42b16c0212d1a48b3502a0f577347ea9c60f3967861269d3e6abf9dcc03a9b8994ae443c16259db5c5eaba05964d67e3a3c744a2244e952dd20d0b5872d10f22b1c50d0c3579b83350603e0360a5d432a371803b95d2367446312b2fd274b21c16b7549637925aa51398bfe104d55b5686e3852ba3f9f4dc32f6961ad792ad34278da9d3020e814056cc762520c67056e2c2c4a1c44c5a22b4ecf3bd419322f117c556a0e09e8b66d58402f0133aa2ccb61bf39472e0eadc72f5d389649822913071e768a0b2ce07025cdda5075d47d0f3f3a688b7471ca466aacf30c63af770f2667a8ed685a69390e4f3dd643b9b75e1b91f18f4b2cd8195d2d5db1640a11df4cf265d06cce4c2d1fe55fb8537c34824d29ebd065063bc10fa079e12177d8d518d8ec5040c109230b7308d2141ae4127ebb7bc43e10ecaf5991239242df6ad00338a87458ae88ac2242e78e267a5dc7339490262001e52b7c0a3cbd663e5e3c1c5dafab1ba4021348d0ca75127a0b911a5d3d1258ced4b32804b26f580cf14f7123ca05732a15096caf3e1a1c2632ee371f8df87c4a5565095438ea2a76663a0c758f3e67b97c674062a1945c4dc541179d94be6763e2680418b18549a74b90747be3470f5fbebb22ab212760bcb24a0a99d234792e0612262378c04917021122881f984b762deb5df1859e796c8c577b9466c72d79a9c7154bddfd3d534f4612fb3c913746b45f78348d2745f566ef65b323e4540c5ee8228b30214d27f9c53cd8704d5ef104b035f5303e4c3f190045bb2ff75e704d161933221b732a1dac020441b740e6ab6b67fed2db6a4a4cb505cb26bd35bbcb947cbd76b914d99337418098ae48dab000619dda5a2a9d86241e409d654af8d4e70fad965e310c4c275ebb506966f734d92feb33ad2f38951d017931e06ec8712b441869471cc6428c06510c2e2bcb2f58528ea99734d3a79901b3d78c568a878678c22662003f1fd634355301524c8015786d85ad3f9048dc5e54e9fa3168ef7659c8039d1d7a5e6f3e4fda6051dcb7800bb01ed94f31f6aa14f1aca202c9bddd07168afe133c7ff6231c235663300d7d41015191136588647b46822d19e81c9457fde0612cfd680b347bb5af797a1fb85da2624c6ffa61c33b59ce5332cebe270ba444f06942e4881dab119f2adb83510e48368a1a4e16ea7259dc9965ee53bd642361f50711a2b80b9cf70020d5908a156368f219d017581d2cd5c860e447ae32d4ed7020e2a7122773942034690a9041d8ee5c0a8f275d25ef140c0c9c8170701987117623642b7cbbe32b46c6589c3015613b125cef0365fb47ed43b5ee9a37b17b0f260b34552f4460816b5091952a2a1caa7c8bac6601b4317e03ae41b02dade664401201441bc6debd4923213b44985e681014904d45d8672d34d91c3508ebf6c30bf94d5b4cabebd65caa07a46e9f23c363e7b00c72985940642b47f2779ec0f63efc30154aa21f7f6250204a117203780c1e4be9562b47a019d29ba7544d3f1465197f5a14b6b514371982d57a5eddbf72efd2674d08a405732550d47555291936c58a3b15f3c8385391ee2c7c2154f360a878e16c2ae98f7bf172764253775750e3a1ca0f5bac2d01f6050d33f172764253775750e3a1ca0f5bac2d01f6050d33f172764253775750e3a1ca0f5bac2d01f6050d3335ffd5357986bf3f3fe07b1b7b0f2d3939013e54a72e5e016dbe0e7b3fdb87282e9e123b15b4dc76c0a80c78cc6a5f2f6db7d83ddbb9845da39b7d14a537714c3232bf3f17b4912a170e6505cd62714a7ba8f871777c5b2a3e81382f0fa90e1477be2a25eafbec3dee8f7e388a96162564911c5672eb1e53ee51d1683e2c2a321c1d3e204a49b056981dae06fd342479fe056f4e6ff4281f476df91110d5d3080394024499ecb33bc4a324631a4a7b17baed860ee04b5e5fd2223f37e2ed6169d6e25014dc3828371fe17208f76d23231961cb14d28b601f20e2296be190205089569e6a4e60ea0c39f43065cc16703617be3e1988a4a21c017ef06a1b67cf01c8e6d3585a0bc72cd79522403905cb702c9a1047d6cfd840ebd7160ec7fa64217bd2db0ed31d7b13a8957352ab7d353c8516b24a376c9876d7a906767aa03d14827ac24049bec074d565e20ad5c0f829869a0437fbc4b02dae0342238c33845e1a059d2db5da2767ff4eb915a2aac6401a18990dd15b6f645f28f91de9a139270c17e236ae232d36f9d16f2b24b946035231ca27f7c9e22de1085c0a39c734258c900869059ed017b98720195404de67a15613658812a6198b1f0d6e017c133f2b8f52491335ff307aa73c17b556ce295dc722660b60690a43faba089226cc7361316b6ae3d74a182ac3a1782722ca59db82600d2467ba7821d5dd04dd771827a362453ecdcf4533807b2c697c4ec61bd894e021ed7ab0271c2e4e6cde7ebe17887cdd0c9fced660fbe142199c88985a80afe14836879d2b464cfc07ffaebd7dbe87342f4e24292a7bbee412715deb181bed7e0444e38a1a61d64e1d168e0d6fd3fb51456eab783e4454120d5573d468583c77060bb99442e8b6bd14c275d83069ac5423cb8c1c798d87241c52ab423047d75d7b1c83301ca7f225763a54d86a4d98af3d8a8804540babe12f141b2b1dce18fd078abcd8503db9225dfdbf524f5acf235457111b039324be14644009168a37d51d8ffc636528b7a40a7bd1443d0b407f0cec79830b5272bd0efecf0f646e638e61adf49d1c45c2fe0d98f4b52e3397b8528f0be14c8438267b1b784f796af5df48b21bea1f9986ff2211a66d32310f8b715d5d42197fd3db07043327426196ee2d630c8f5e7cbdb62117f34546371daa7d6f403f28fe3db315a9ff5804f7f2c038920e08755501ef1368511d7922b87331efb21f71be1bef71f2de7033b88acd03bc23ce642f023340b67ef91ce170ef5a38ee77105b0ee35aa519a674c83c1774dfd49f28cf5c02575f48f56ac58efa5dec29cf70e4db8a509fce443216b6136dc1f8e212aa4dd315cb0c6d1acc431d677e152f00591d811925504a67caf6742cdf4b822267eb136877af2513039f953864b8ca6df903f332ac18115ba241dd5cadde667b4c80217b5fe9d122970f0108a39f471df93ffc6fc009c82f6fa3f653c8576c4edf8f822f4f69843ee45e87605b746078ee40fa6756e62a6fb8537e0976a94e3ba54f8f623bfd1e78285da6655479731f7dfd142d05d3ac36e6ae3d1594561a4d42502a42252441523c67fb1d1989f3760d98bd2993c2a646c4a02819a19ca50f1a9ef94ea6115a4cea649306cc18b144df897f7792b534264bd65e0c2ade3d5cc677bd3e79d92239af2cb62994a22754ef0e284fd024d020e6fc1142cd94e720058679181ccf3051dd033b3bd6ca4434d9422458d3f978426efcd72accbc1467b8efdb191cf44f0d3c3d2d6582d151613c09351834034132b3800f11a23b6116a6a6284fce12300ca373a2493743863c4789a363f2a5454ad7f7fa1ffe09c13a9d820c405155184b2cdd532b5595353a4dce8f1852e0636e955c815eb698cb69c976bd33f3ffed45cee6af070dc0ac6f0d8a267a0477a361dedb163f7a84317700eae61ed6e52551ba53dc429c49e63a73e8f612d46a3226a70c8013162e2e2bfad54c655b88e5559f6e1e5d4e63062a6eee7b566f80fd39d5b61b777cf59c559a4ff54b14164a263f395d7693aa522a126835758b5ed44ba7e9947a91c10a592a80712a89af6e4a7dca0d208a929d70f1a4fc7810f2b04c34571752869e5e7a355a6722d78f9975916db60a21d42e338c08296574a2861dc8bb396d42d2957a8cb9d37126749c21345209719d9f096fc2d35100b0c1347c5b5bac61d96650179a9d8f34e2301d3470c2244de882213b45d44e2f7dd34a10769b474a59c50c767041297e0a574d22b347dd4a32986404a6a5f976b4e1832c2e1af920ae47ab5db8248f453f2838073495db53e1d43579b2168907286e3b1a1443c01ec852832ba2483c77c0d3c8213cb7a304dd00856d60fc716b5d3d8f7e1a495a0df5b55605f9815f5ced996924fae0c30a2772986e233e8e43753ad16005c977197bd0ba3e5c286d1bb011e74039eb765b92e2ca3198ad131b77981516e3d4ba6db28b8517078ec507c3abd01dbdc9a045feacd20a68829e06c2ad710a9ab80a76979b684800507b77d35a371f7f12636a8ed8c25e7539252d8ff88f655b2db81346605b68796e975065c72a61c4be5f6d4717e7755820613e06c22f0f6c469b24fc6d2d26c51060096bc64800d987001933a8037b243d6421d93f91021331eb3b8950541cff50b92b09007e79aae01f6fad796b573f88cc007bfe6300f5cbfb2f4b7a3f5334e1105d46bc3e3f402ad5245966213b955434697691c031814d135e6f83091d92597d0674c43465b7d70c0b2342d13af4b962060171d931a9ade372f3e2da7c7828446eab8d2436291fb2632b20526433699163412cb6176521b17ea8c52737db02ab726839de254b37694506cd52140d4ca319c6d2712bd82c82793bf76f14bba75678abf68a5a03026a55df247e16fcb3ed0629e52337848f1e7566955a63a557da63fe22f23962f66d2f2cd094174aacb853a8ca170acf3e3a4b2738564a225f567e76b1c27848db7f07ad2508749076fb664782d139eb5b9f3dfef5e77996af4b0612520907281d0220d6e916646b6cb910e44fa91359675106e9a47b51cbd9c11cbe539029bf86e22b019f7a5205a81409515abb42aee7fb409328cd2baf27494ef9efda205c74455292b9641379fa6255262e7b3f2119211d4bc0af2d7e1f863e2dc0517a9485bb013353ea5104b91276864a2b231f21fd4ed2824c15248fba77004d1e75eab46e42124d0457bcd675513c0609654c898b578e6af757ae50e3207393f248f72f9d293338a0400edd2f0b324afe7db1d9d247eccd8f7b76702e7459ff1044152b565347a0b263736af471b3ed8154c7e3997c6db0651a09c27835d320e66b824a8d0bc6b15d0bb5f7040ec6d2ac6ba8440f47c730b765c7d288235318b65e5e3fa916141e06269069ab68783d356ed0c6ab35cdf863382e3ca1200cd03b0fc751d7002302b04d00000000000000000000000000000000000000008d626434b6cf062446ab02040674496e1f63a25cfeffff0100000000000000000000000000000000feffff01000000000000000000000000000000009362e720b87f7f0531221f50c2129718141b704fca28464c5cfb6c4460b42a4053409771e53126553445785cc30dfc0d6710112a83c69c5789046c40feffff0100000000000000000000000000000000feffff01000000000000000000000000000000002fc9a842cdbc0a3a9e783f3d9d07e65b55031237d027681b60a0320b4fb8a116f6c9823f3e82777b8d626434b6cf062446ab02040674496e1f63a25cbfd85814bb42f03407105560160bd24240a10d2dbfd85814bb42f03407105560160bd24240a10d2dbfd85814bb42f03407105560160bd24240a10d2db1ba344d7a1ba7747ca7eb7472652c5838389452e6978b427861e96848ca8a13e79f5e67b2684842f7a1d42b05fee65f41e0f63ab57341397d765a4f3aaab566ef0f356a13544c6cd2a4076a005c430ea1329323af041e78db3de50120a3ad5718cf2a7885cc5f268eab6a45068f886e92c32c2e3977af74818cd52e2ebde153575fb11b6595ae2b167a0175818cd52e2ebde153575fb11b6595ae2b167a0175a6ee781bcc6ed53c7dd568246123665b2ba19705bac658498277651ec05bab177b6dc84eb6dbaf7b66eb4d2b100f856a8d5ae22edc12983978fc294c40744851778c002cf6f4263f2454082d73856c1bf4000c69d476146399193433914ef213dc6aa71842952e083598c260bb71b222835bc62ea38206606477887b75d8342b92fe1128442b09589ff13e740e080c084abd2604e7807b71e30a2531e7a6b47235066534467d770e336c48342320527665baa871bcc61b211dd19b2bfdfb713a2f38ca603b913179762e22672630bd4112c05f78de8e350dd399ee276587574f0d88565ada3b5011c881d348b2525264174adb00be7fba41d2adfa47afdbd2397da83d5f91dbc23b80785b214ad1f461be96583b3648fd4f5c96586d38f332547d09512eee6ab96b4a912311e538ad243511f16da61f9b7b79b1b43bc929ea27622a416be79d407e49f9e868cb6aa72d26ce370c2b4b2c76c6e2683dfbf5031ac9bd7c493f16d46ad863b10b0a187c6cdcdd782c857df1196b60ed7a6843416dcd5ab350ced21b3984b5932984e0e92194f9e425cc8d5558dc30335c028be3423e670f6d96b573413024d553316bf701e0fd0e6c104c661e71f8314cccf32a5bb94463460840a82501813d1dea99f859a9011c5003428d6087b8bc6dd7538162dce0e96a0db36300a714343ba6f012316b14e429a218854bb496552ac4096e63bfec4727445cde672acf8876e19d0054be2335379821966faba27a574ae47461fed4e434cbb7752f38d04439af6ff57a4dc6da569eff25111076555b9c1b0f14224c68098adecd438d8a471ea1460d1f776a4a0aa1fef24bd06aa1522b227022e0fdce3f47b0e95e879f0b3b1e06595624e7b13ffb5bd9300152dd46f94d305ca74f7d1b83b5493a822a7457b82a81251862672b94a3c72d6025b86e9c27720d9376c00fae6f4069350f68499fcab759776e6e09e5db277b0d914849feffff0100000000000000000000000000000000feffff01000000000000000000000000000000004d05663abc5f1d6c164b610dd0ddd42b01134d5947f3801fe5a97c266fe7e61741be7d41c710891d74f2ab36405bb5726e72b16a1160ad52a2b5e251350f68499fcab759776e6e09e5db277b0d9148497087592e0d9fa71d7154ed052622b634009b340e7087592e0d9fa71d7154ed052622b634009b340e7087592e0d9fa71d7154ed052622b634009b340e2e0a331ff4b16e60fd6a8f6b7e58da3a7d9cd908dac8997b51edef0dd9719c3e8d498751de151b328525c9700e55c5613c00d5568e615e07d64f6c408adc1e0218182c6e7d2f8d394f876f2d88847c46de59140f85c357439708e445f9833a7379810a22ca999b5f6504615105c70a0ba9c53a6080c93b0a112de10bf08b707499776e41d34d93640a052f0a35b53c2774fbbe346da3d1509e327e393d02455e54a87e3b8c235c4287670c78cb5fb832f8429249660cfd50fee40217427d9a1091fe6e3c527b4b3eedad2d655aedd33f614d737173c81069c7874b0509179f328afd9b669023516603e5081afe4b7c380444202e62aa8223931802258adb5106f789ce3e940f73088215a76a8a752f0bae2f9a6915061a08a8aea6141b85153393aa35773a44147e7b8099722101ff4d534cf43edf99b6199d75f64c6264840d9827bc3e10871e0395762a4fd065085829723c6abe9af160f5c0b84159927c348fe6154b6cfae27e469d6e0a3384d77627d9146417c8c6445d97c944a5033e231f4ff62e044563166e4cec283b1ea600a159e215732e590abdfe5f52057d6142582d4d7602f3a60cb3350e1160452f3ad0365d5fb022947d18bbd33fc51c1b410851075fe24e2316dcaaf313017f121ed20eb809b7c2103513408432f9a7d730d951c50090903712a76d5a6a45dfa2653c587a5ebedf5e472be7a47c9f77555119ca9419db83d45ce743af1730b2ba701344ba686ec1355da0afa95a12686227db2ca46797df580c029b687ae22d305fe1ce503b70630507fe030d0faaffd16e80d425393adbb0166977556dcb43d12efb04d3722c178c02a4fda420c3b22a7edf80e247a47c6355ad38561e389da75dba54fe46ab9c2d59c7a2264b10ebdb26f4402a47bb928806d1fc542523205c3a1212d12c6eae276d6b98d8029cecb016339ae134f11c6e32177bed509600e5723ed1eb2c26a27b32f33c5e2eebbc861a8262592a8d70bc0ec2d5a646a1d8a84b2d32ff7c491c2a36902f1b0e903a2010c34c67637ecf992c029cbc385107f54be54af13b1bbc617a57448b1017f47978bf4ff063327e56729c434a15af91cd644e5914686e622f5052439e209343ef7b710ed969000137736cbf1223267ddb3cfec6710ae07de9346d44aa2eef4a85463e8d1c586b15c53b6da8b465d6cd9d3aca17393e5238d3397d3df434f33f1223558dce3cef6a2b1d2c5f8d142cc6d575b2fc88310b65a01c169c1a5475e87d684b1d841005e5991f90febe774285246a4aff433efff42b14911dc82f5e7139376a07ea395bfff809daff687c3f871b5835aa95754ab7cd66e205f564d042af388bcf2c7391b0b337ce3e592aeb59a507e16f4a2a1e577817ca7c7e577560c2469985fa0625a6062e568806541bb73d28f726c456aece142d1d140519a4cc435789ed62708fc60e6741efb05b95b9797ab6424d08b893433a99e0000c7e4e5b4cc4ff7a69b495281d86db4c5debf9b02cdb10be5f677bcd54c205670dd6c79b0733be5f5849ad822702a6ad06272d4343095b3b657ae1c55c8cc2115018077a65177e573d3b68982cbcfa162cb2c9e36167528d083fd357287a4173335ba5e125a4e1651ad0f6b937850ead01695da33542e6826436772628de07e02b2579492968ee9a7a2daeb9475a196f0f43e97b4d9a5cda393684a160d43631733c67c903a6e94a2901a2b72ad868a92ad2934f28a9ab6b5c594f092f1943971bb14d9c162aa20a7a9a33c11d80668a5ce3c40e4d0cf748180d54570139d25f6fd657db3890b3f65f5a34ab08599c0d5748bd3d1935462c08f09bb22f9357c842c62643339fcfcc57911aff43200de6582d0c46361666d76ed2bc431e998fcf36d1686a03b3a07255146e251b0d75861d17f2bf467f956e6e9f05b81cee76254aa9bcf6726e5aaf58ea79082fe0636d69cd00b5741db9ad698edcc064f26341352f00b02bbbf9b40abd37ae6bb067a84c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f51b983245eb971bf700c3645a8ad8586c6ecb4768a5b917b8b224556d3f7314c7c53b776f50ab1657835157a7dd7a3b7cbeac1166359f2cd26ec439067f7f64b75b825f76a7487c2f1a5b7de37a1b63ced4fe136859ce737eaaed569f440d423a4ccd78399aae1ae11fb45cff35114cbed49462efe8dc4a8107362e17a49a23a813dd01cdcfb926ffc5d214a5557e2a3d527120f4d37d3e702d47171b390e1be374262f7ad88f33449cb20133f37a2bbdf7562b0bb66327934e7308e9d2155c7411f71c4fc8905fb9fcd3449013c032cc955f0364ca814b108c74620000000000000000000000000000000000000000669ca64d5b9ade64bf794639292e9c7a6e358727777db01a58de4f51f2f2ae682722591657e083647bd4c075e1ad6501a2910371c6a03d31da0c8e634c3bc965da34bc283686f86d50dff550072a0f3e8a7bb71a4bba9656cb1a1827a933c2295de3593a57a24403d93f50347958f203fe35274924715f2934ad82222bcadc5dad34541ea29198211848d34805579d131f8bd258dad5cc4a3957c14b01a52c311a755348704c124b367c986579059a5bbbcf3572f485ff0c5c9b5657d0cfb7054be80d77d5b90a300ee308447e9f95567248315ff0584c724478ff080000000000000000000000000000000000000000c484a123f0ee8e4cbc9a7a10acb26d5b348b3c3d0d76b90b87858031e1b4a849c1cb836736f29d6e0dcaf5148e587a33efc39c02b950a91ec2a0697b0beacc3eaad67675e135e917661c7f026841044d0bff800326f974680b63150ab8fb734bbd015f669c1ea610de6863600b758b70a1bb000483538e31fe9c311782d51a3fc297de781272935ba389641cbd13d711eac28b4f44ead93c8fdbe96dd600ec00d70d5015b48d1033e7e7bf06799d8a1878d6973dee60fd37530c3f207454510eb5ba742c5eed733042561327b8e1c658e0df077e65cec8702464bf24000000000000000000000000000000000000000001533b56c18268277ec28d40380be725dd569a6a79b8254ac37a80636303131615c58e31388fad714a788d7dd7ba1423e353ff7d859f93261b52413efb2d4520074c6b4ec5ec3330c797e12eb0e5a2189836052023457d2962a2773db7d4061a2d74544773cb5c0f487d4c0ea547711d21d9760a7a532f4c1ffae94845af1930264a3f6647f3cd2f622e7519f59adf6864d7af7722501935f552dc02bc7dee2d504a102022accc29a0a8bc33b679672259510e4c69102f6f46c60715a4eca9314198a4241547fe1227a679149c13c1443e4ac266421aa03eefa8a8390000000000000000000000000000000000000000d88df318323def5d875200184660e8443101c43c4eaddc43efff8e56850b7a4aab12e60a153e0d23d8bb2c0104d48a4eed16350568ad4239aea1904eed43d972f3d4335186fec615d6ffef7d5550770e2d27667e474291037ef9a12b025ae67289f97b4fcd5f730a61feeb032444310bd01d515764616916d9684f65c0fee6710fedcc353f70b0652ee57e0d77157739094e865a12d6d122b303b4620d7cc62abc95f206b5823e2c53af4a71fde103526a681523e8cbbe23c6bd4b07a1a3407dfd917b2e2fc1952353beb225c5d61e4a0ab02a04691cf3134a7e120f0000000000000000000000000000000000000000d471d52640eebe47d081f97e59e9ce30cabe010c40bbc567ffb0905a45018539ba9bef513b67236c0000000000000000000000000000000000000000feffff0100000000000000000000000000000000442d9b5e3e9f4f2314837e391285be0305262578e1bb46183a1147658c914a39d7cf350d541c335921d3c82667219c02da8fee700a630b633ed9a25f83226346ba94a752675c975e958a7a5fdde7e47b85df640e633a3a03615a092e4e6c3a2e0efeaa189a3b8f7445e4d67a7308057a5caf03668be68706052406113577ec516ecbcc035ef24959d14fa94baf0e652e487928308405d21120b4ac1e4e639b63000000000000000000000000000000000000000093cd3c0ac90b091d51cb4d0d9e9d8147c15a9b60de7856582ec96867ed6a8a2128255638220adc6adea17726759fb17765606f7b79ffd8633d9cc831ad671a3edebdef07bf78cd0b945fe327e3ebc5048e474f563ba43d0257d9b97cc04bce0b2d7a1d179052816c7c3d6f39e3d7163f8dcf340053c7812b75fcea7e06e8833559b86f3f1970a351643e031f2634654e3160fb4cfa3cc206b1c1e657c7f9ab7b000000000000000000000000000000000000000012742d345917ae61965743362ddb0a1c077056222ec2031a8ce56662ac2cbb43f8ea3f4974399a756fd469057d9cb94390ae925a2aaa1a405507c045c6fc7b4cbfc3ee5543185f25f1ad8d1a72b0585aaadd9b521bc5e34c51fc3f023306d53b6017ce0ae9a79c22359660000289a81aa47d9c10a593d94553404f6589dddb48e01e64395b247a64953a241680063a0a115ffa2dc00a400b2b282c2908e8864d8a292f30cf711a4e56387872366dfb18243f2b0fb379bf5d97d8fa6c9229ab35efaf843c22e00d02a812eb69e6a263052e9f144fc04849595083ef2fe24c147105a9dc195ea6c03bc2df3d592b886c6afbbc156e4408a330cffe155763268e0d6764c703958ebb362f43a936885ad8181227ea2d6e53a962dc27c2083c9fe91b5b821f728014a04b1a336c0073be8849fd4ba258c1c4e1478418c30dfa241c3e8c5aac382ea45153bf3b1149980a4b780a8587396cad5b047612472456a9cc17cb192528b57b655fd542911e3e04e009c424886c2d00b509a3d09c1cebf54d6567530f5fb5b7735f2496a14be88dfd4359d914285401555711e5fd5a1c9d7821b06c036edc72bf395a54a454968dfb5df987cb66d0d1516d4abdb65f0e792d648c61223b82cf4b5f261afb56ff09312cfbd56f029514815df20c5200c452ce6aa5a53e6c50c7e4315df761255500767ba5b54637238690092003b11c60ee827c90eb2d37513b4c7506ed10181606421031d85743d9f84d6d47685b3ad16d53026ce05d6be3badf1804b4e26840abf71a53851c4e7cdebd3aa3300f00b356947d58ce3f168f4b115eb4fa0a5c25cd001198f8420e70ca7e77f6627a220a65005d9697bc4d0cc7ee15d2ad4504d648e82d42cbd721486e0c7d6cbeb1421fa7823bf998855aca78eb663857ef67579df50d52188c0311fee0539f28d11d83eca724501ba37b505ba03f2c59d47828a92a242a1ec32705cb18235e4b1f6c72402e29ac3a307bb1aae73d74952011668fa838f0dddd56a4b7162cf1458626bca0d609ad508a3e1af3c4796f78f7251835f1084dc29767a55b5624085bd218ddc493279e27af7a642fdc3f9eaade79a1e93f608d0034238c71c12a50f6010f6ed9b22bf3c7be1f7f305e249835b54934741a3deaedae23a2244426a522ea687846a92848c87c02b082ba538a8d440db60bf16296ab9079939d491b7a97242e81f12a3d15eae2322341142666a6eb74488a25490c446024ba02157172d29125d420b172df889124020c380c2c07014834c3263dedd5633738879c496f39dd397c04e543fae259425a9c7b62ac861100c91e133f5416526e35345c3069e3b00bd38e7f595c45b0509f9e314e6cd07b0cf9a6cb3614332768aaa7e21b4cbe2a6365267f41644f7a5cd1f7e72dbc88a332242cb64aba10706e0b6bb2291bf03804b27eb034528f587b9a30506d1da8ab185cdb3865cf6523315fab6f6e1b42c60df811405f0ef1400fbd55676f1d079279e3038a2c99cff76a9057b75a6b93c91b3333f735ab5b6c121238da7155dfc2328a54560fbd488d12ac60e04ac66e925d731a5124de3fc44a5545652c1d709069480f004865f8382163072c69b022d63912493c365b05ee441a8db22f64a2220260c27a104d075d6dbb6ca42720d89e2ced777d484b380e3e6e04f156c33e661bd4409e7477f9aa5529d88c3eedebe5233377796608e613728d68c079d7fa5f719c6728133154412c6ccd6f70a3abf46a552a1d0ac5a1e9703b6b6e4f82b16e3afc18a013038b61549a06c1531e5e8b12e9e88f5104a7776ebcecf9353c921a12fd5f39633cf7c545746b6a112e8dd44434a4b14ae3d3822b70449317270fb71c1ab2dc5aa626f8535ad3712278a6572a75bce7072f54ec3ddc9c905866b21668c562375cceb1290f99384627a4cdbe2f5052d94c19aca95750f2b1697e0bda50c5572166071c1b47c4c19535d9c588489665ef25dbf08f217497cc3874a0a76913cce6685df600647d09ca73b751ef0dca4b4a72a1bb082309d92e667dd4a65bba318a668581db1736804b23005c314faea7202870fd4c0e8b1346532f81ad0cf89fc60e2b9915134f02eb724e69c0777ec745170b70c5141fb30668d3b3484e6d1b341100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000674cb7746e112c114169e677cf39b450f4232e29545e1f5cd9f814633f2a694e63f5761d37c465083e68877b0b26445b4184792247b40803316d9c0cf64bab051cf9ba3323106d592c462c1cdff00e04beb9915d56a8337b67dd6022f935ad617c17c852358ca3755e3f953af188084b61b22451dad65701358ca3755e3f953af188084b61b22451dad65701358ca3755e3f953af188084b61b22451dad657017582164aa48ca03b972a712f0068456fd39b7e6d52c2e9269145e365a61a57713228436ce784aa2b00f3176af4be9775825d8e5c4683b633812ff968cf1e443160f2f53c8dc32d4afb018c70dda87c7e573e3b7b108ddc20e3e97f00482765403d21f6252a26215a20ddf7078900c425f54c6149db99534b8494707214e7d675c4124a24c16e5058a14d821b0c462837633935227b6f301a9b27464c5957515ce43fac6f904c0d7cdbe18477bd5c0b5de7fafa53ea69526c92c8252f6afd941bfb09900bd33dd9290ae049518af3a149705cfe39ae3c1f710eb7b3153aa6e445e1acc57389fc59694775550cf66849210e748116fe7aa309944b6f368ec1f37935cfdf09d883d102c126d20b6379140ef8f23d6dd5b4da7ca57f2864296a292e04c8f702bbc3d14c8423cd1d8e717168482fda13af5a8b014d5fa92c76f03a39a0868b47bef8be1143e36a2e719ed83e55d6893e0b86730baacba7739bf8a9640828f37e96f5b94a23761945a4ed25259f8a33622dcbca6a41ead623d38afe39862cf86b1283c02df6ddaf6aef670e21182cf9469d0e6b5c51a0bb31ff02593275d8c74beeca92180c74d833b9b0d73736bdc265ce14d34bc814b5427556c43d274cd62f6535596de67b447b23ba234791f0370bf9f00040b660397b35b451132c51197a211919600d297f7e3b95b44c9f02054f27f8966228932d693e33ed775676f62593f5643a0000000000000000000000000000000000000000feffff01000000000000000000000000000000008249b02d48ac464a4a5c6c77910bab7231f8690adad27a064a750f51aaa4657364b54f5d240d92077b7067330202a5194615935482ed9c63fc5db618ce7a77003a328b6c0d9a7665401fee7966cc356a1390494fa574862b5e78b024b86f67585051f56c604642197237ab0c8620d26d290b6f1aec234700976e67183aa7d971dcd9761a5ea1e3217eb9ab3eb0033a2d3c8fcf0790d2dc1cf6d78b4d8fd0c4490000000000000000000000000000000000000000dfa60226bd145b6ee4d84c24a9268631999ad52783ca5402711c1556c561d8043ddbd92dc5d3e46d23271140ba55976e4913fe366120fe51a380c10d0dcccd3f17a4f41a07946a21693adc77b848ea2bd1380d096549ea6cd739823aa06d7d6cd9bbf96800ab5241952d2f597cee020b0a537a08d72c29147b3aed405ec5377798697744317c620a79526450f91d57601ab0f604a306c81aadf6302a6e8e3c720000000000000000000000000000000000000000c7050b0b4e390f29ff4ea90347bf9618a3257a785490ac23f4ca504e98676a5b226bc25d56e58f35102414032e26402523504e1b1535d2535460276fbfade02fba3c487b1f3ffd14354c53205edbcb5ffd950d3a1ae0b65d5c8df725cc98c250893edd7dd658e557c3b43a44a87ecc0ad9278767a855570b85a161162f4d13231a84bf60a875ed731974e00a0aa5de335a3d29273a5eaa3ca68e6503befb0e1b4651572b86b3d53145c9024380699965872c9063caf1fa4353a444405cdb394b2183542541a9884594f78b0c1924827bb5adc73c2151d875469f1057625f053bd4826b57dd9aa4630e5cc060b766e72d28255567872d504cf53ad420bfcb381ee66f6b37bbe9a330eba5e45ed9d6106ee427664b7fbead37c3eff660fbd0854336dab113407df71cce64800e5513452310447d61936c8a1b574a93578780c65b1c7018184279d749698f26462eaf635180f8a8283914be5c20f3222c2987b454f38412438aa93e088fca496648db1f2248b2093c37fcfa16934b7625ce3cc919314fcc5176102b5fc067bb4908cd26040808380b30e7862a92173c4a1dd987020d48b36edf691d636b6a2e2c3a7cf770d705313be6bea556ba624d0d3725054a1bcaa756b972bc636822d84e36c723386b445e19d837884fab237c4b1dc16416f25bce332d5eb55fa76894096345850cb1694d5b13d3344a2bb5a54ef695f30ae565855b4356877e7626c1495be1810c85521710044c5719482fd40ed49f396f78d87f0c58f6f54acd6477410a6ad53e992df46170c6bc70694e260e5a617a0081df005cc14fd440683eba04b18ca44645d258096b95a7661fc0ea2d4ed30858362c0275e2b1b43da2202020efd65a4dc00d57616078a166fabace21100f792948dd957e190e7e17e25f283a529eda08cbffda6e3d30cd59e4e907024ca78e4198e8a32b136e8915a223af497b6fe05465335a2997869141c955cc316d31971525fb9c536df67530828d3434d48fe5136b8cd91942670653ddce8d44e6093e1258502f10c76c3c4a21e34911f94e8f419193053d18123565a353077d7eb4fa71b75cf234fb807875cfe7df0ba7157a7d4506266189e6d70ffb50756cfabd897bdc26880ff2454770e7e51a3ce95b1047537f1271b61cdf28eef3404c4b5cad51c1e0ad423f1bad747c88a3522427da5cab01b61b24c66d4d05aa2371b19241452558830789a5b33beeedd056d16b622295cfee7daebb026d1ed6fb52fcc7b4662c448549a6803d0e6440a6111056bb40e05acd76a3191b65b7091471ae35cf324416d873ee852959e99f523e023e9c197afa5e16cc425d7842c0677b399d2a4245b500337742e56a938a790c37992d6082148923c36ed6241e872806c7ba5b5e2d474379866a78484621dc22691d704ba5073a6210ca0b65487c6f2e0d270e0074c0941a31803f4376517e3ff691fc75ff5ecf5466181c7d1596d740b8678d1c80bfc30331b13412d757ac7d59dd1b2337571f631dabb2723b5b5f7c5a3c0403449eec3f6d0e9f0aa401e5039d89c94e24d6932135e24d6324f6c575bf10d96db90db6794f325c63bcffed6caf85a63506483b5428c4ad51bba2a4716904012c16fa11224e20625a5dec880bb4a4427ae133333f75100d616965dd65cd9a1b1545932a0e35ac4662d861c76ba95bae29c0117b7e6ebe6d6670469d58ec4432443052a615fe4e304405fdb43d2a8afc4d89def236c7d7c711d9f9503bb373757110b1c73925b2da207f48c83d5693942633f3ad0da970c503fdac061a5acf800243657552db501346972b5f49ba3f044ff8b92a0a3cb2a6794dd7f577be5e2046ed3d575052c8b35d0b560300a54ecc29f827f117535cfc56282788799ca010341924125c004f8001737629449c925c4646d2764994aba33f43663416a8b73f25927721745fc91d1d6ad7a21b794ac5776d95a1351aa9410e6cdaca745ac7bc385ca69c531d329122e4a8bc24ea62d00896d4e31bb875066d06f30d445be92b4417a87e6bcb54534101c7aa6c83838060fc19a32fa7128d05164fbf61979b310792e5941cf14610187e00e2342d796a1351402e15f09dc5301a84290a952a78048103f36cfb69946c4de60119802193450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065920b3d46800867c827e50fcc5df44505672f4c9f0ce077e310ba51dec8576b78226b5f33b0d47cf27eba1a7a9bae7b0108354274e6877b575b357caa2d7613eda06e331474c164f63d880f36af90303b6512363c36b03a2afcbb5b72c75f3eda599f596579467a5cb14c033e11b71fe58c7f14e773ab3fd838f20476620e46bd72c5668123e80bcf613465a5c2247c1915df28b420f6271eee575dcea85702e02b627ca8779745548e8646a4286252ce5e892ff5ec5803aeb0ca041f91a42afe1f6a474761533a7eabab667b61aa3cf85b5d312d84377baa896a514bc8a11f87ac325ebaf9f16871f5247678d3704b22c6271d2d03cc45843e552b71116b14ecc291791dcf5774e4917002bc2f0711b0b7ec483ff484612b2fad1aaca0ea482aff7301f80e692eb6ec2e76880e6502cdc5bd7bb3f2c90fe22e8341d8f39f7030bf1b60d682351ab607765f6bbc8456d05148134167191595847b0576d07b2cabd36848da47110e5697be5ebfe61b74362c9c7eaf4ec907c980c263ae42c2681de5da47ae1b9c2a2ec1be0facfe3a734ee3da2e8c631d53d11aa8601f616e159b8d942922b95718d7cdac43842c295e83affe69cd25662c59460810f392e22c5e0e354929f2b463126e5c7aeddad128a0a9e004eb1ac52f40db8528e6dc8b3aaba05375ceba2513644f924637f8cc619ba2b949ad2dc5638b502263b9cd220b5da7e55d5115c872ad82e46825cc5f5f06eb326ff2062c29ebf96b7db0db102cf29a576c63dba3280d920060247a385e76b8a8239f98f84f9174f05e6a34ff519f628e4d673819147b3959197097b933ac0191753846ea680b3da83d7592000994475d7e12f3047225ef984512f5b55c42268f1a61df040c2bebaf7a5ca88a6b06080c35055df6110e42ec328e914e643235534243cdd011f57ae512019f2550d503501674d9eb2ef9026c35c3ee6309fb1df34a28501c27f104e954c1650f21801b8d089009f252f49f7104fa7b9c5670f260550cecf2710b96f44a3bdbdb23824c934ef91205793923974a347e07440f5f545b4226a42b84c8780ab390050a286b907cce4a06117c0d4e732f4103595dc4e34767fccd7a47b36a1b096ccd67020d6b768737ab1a3cf684655ce0a77162d770635f14663950ed6e06b80a9d279530a352decf1a166c8355773b81bd31df97a126a0e1990f985a0f4cfeed6a2a3786de6b17e0ad4af8b215642108e0322f100578ca3332159b6ab52299e623758b7f1e6f53580178e343410f50b5f734b9f8f107e5136f16bad21960291f0060b28e0d00a9dfa514505ee5121b436220fa25495ba7c0dc26bd805802e83ca41f6c402d64429f914d33d5ea393fb4994b57814a7cebb6e93dda7f7473068f184e61f9c30dca27947338053f3730bfd6309a11e25257bf7944ccf091277d552b066e803c437fedda12c0c2660a9077e73a034c3e28520818347266526987139523dfe6dc09b7e2f9634a7e7f092b8d1306e80d454ddfe7505ba0d0493d8214de054f6af83bdee8af6d4a738a4002e3bc5eadfef407b828d9122b47fa24af490162b9dfc24916f0334f34e1972311c9c115c52a146ef654b238f59e0124510f301c120d6810311651446c86d86c0cf9dd6c4dd33848be0bc37d1e85be0155f9644b88b050452e3ae67b07104a2709c11b20ef37707263bad43415222a73c57f1256fb012864a5fe084aa6bf684f856bed435d436f10cf700b39279cbe2af612ca73ed101b22bd56020ce2e12059ab3b6c25db0a6c491fdbf61087fab13dfc7e6c320d2d8c1d15ea4121676b491e2c47796237d62838a215dd1cc685bf3c5ece201cbecf0172beb27a7eb52be3661ce7842b7c725c436f9ecc49939fc5169eef900f5bc9c54f0dac3b6689b4372c184d4a53eeffb920019dc94e6d9ed741e0eb8e325261920068f7ae7e5aeebf0c40885770daaf783d40004d4fecb30d5faafd5f580ca6e7774c92fd079e312c3484848c3c889ec55b9c5f47580b61930bb96ca31be394a52102204070faab3822910be714c814b3295997c87e1ba56c255fb11a468b3537324d019958bbda27423626ab2c3f220568dbb9645bae488c4ea48ad65c5e0dcb312b48ac6dfced65082cda673575850a16c0e85c5d1f73f64ab297d25597a7182d9ef3d25a8a63353dd72c9b51004a03207f64f65ccef74908559b2214366d2378dcdaf8236d19b062aa8b205631540a3ccb437f5a0ee86e50556af113968a0c2f65e09e14f87cc876113f5d527d0ebb37dfc84115ac6a7f27320da8375987b022a00e565e6eb73f2b541dc356264b976d68d721394b698a3449601c4f75d6e875dfe881429db9d43797395425c1df953389290663888d0a02bf65045b86ca1d7dbd3b9a23fbcfbf02e5a4283bea578b65aaf1bd4640d34d233e90b113500e3879226f30394855ef43e6d44909bdaec962b655580f18c633148ae73677640b311045e2760765e1d05478420c0a183f5c4cf243e85d51965957084a3c10b5dcda29b71cbb66b3fb883544cb8879b04020036502ea630cba013e5f81c04ff10cd33097c36a67c59e77315e631a7ef2d12f6514db606b0ecdf42bad678e29781df63c0a648c1dfba7a7611c1b8d3630091c6c54e9de318d079621bbfd5b4fd43d0f4d8e7cc93da5b58a62ef3de144974bf84b1d376a10fd3dea1f9f6f1560c10ccd14a5bced1b3e4427603c51c82fe9e1217eecea9e3c681781534c07e04a18d2746b1babe24e14427d18fac03976ca8b4f379b97057c5a221732e9fba875f52daa65dfb1295002f414308d57a06ffd6267621bdc9e2777654451f074612feb0bbb4a4efdf47a9008f4440621ec356ae00730b78d4d3bcf89d26329649954420d5406e4b48a17b027272457b9054e02e460764084032f9a818b662fbd7059ea9cd23697324330d54fa2204cbb30523e8cc75696329c00df168507a6541162aa067a2972b26536e95938151ae6fe4ab9d76202de56a915cf5b890227a39f5c801f0d6009a00e0689b3c35f604e070017289f295f08764e4042f552c95c425003724721a7487f690d1f7f3f188ef04b258d3b012f84736b08d2543c5cc2f2620c002e0dd5a9a3668248b510550c8c1e1f0b947922d75e4d0b471d4ad25a1d4445392926f06ba72c282cda3b2c1c183fc5db9a4ca6ce8526d0a4b23f87636633bb63f368df30630a9834516ff6ada05231653f4d14cabe3e432b1c2d55903403cbb26358537725583f33d6670362b2054dccbc644914185bfe27b719a93a861f4e510f7d4e52357e8a310572612187448372157bc0b1d842fd6513028ff026093ed7ba1df25f866693916d64a2ddf61fcae34e3785fdd254b646ed4d3aaa1602ca1d2e2d46138b39119a5c48a433e5336409884eee5d4535aef81055738c7d371bb43d0e48d2d164ae22906d867c6519149b317941a7702726882e682048ca5e161be85cb110c225701efe71b74aca29a0727a2e7672ba6f2ca8fb63c4afe23fa3fe9d36686b497862b83e68738a5d044bb0cc559f6c224e3896612e8d9bd35c4644557ed883e428bcc5155b01e4c61ac1db4a2c4c0532532b1323482b2c883cf1e3c62ac1db4a2c4c0532532b1323482b2c883ce1e3c63ac1db4a2c4c0532532b1323482b2c883c02fc250237621554c8b2bf6b551e726bf6d39951f2fb251237621554c8b2bf6b551e726bf6d399517d5f3f6dfb1a4f2d18f71361d05bba7eaa2ff60e8b6f6a1d187f685b47d5b329fd38a8647848013fbcfacc4317ec20749907080a46c82a66f1f61f2271bbb73752298f57684e0d2286f8b5002f58fc5ee906c342df254015b193d57265c7c33623ad6a7ea2377c181b19d7711a5bf55b9dfc6f3fa3f1d41bf65aed738736744ccd3c0e7249bf8642a4e3875f11ebff0555658d36b9b9c73baeb9146e7bb8744544babe00fd5cdf6429c309251699816892beaf627c746043aeb15f3fb2e8956c84bf544c05f5995f8b5a9a3585f15a191d67e71036c01d45f791eb5c686e626111c0c76b0f75032e71bfd06cd79cae4d1a43836e04d3630c3d6fc02bacdb41415d765663aa17ee423042667cdb00897c7799d6572f059062b69f1072d51d9310778ed40ed656be18e527d0482f4931394373c67da4c1cb7a609bf935b2f01a3775636758c5a9ed67379e2307c9916f52f968c203e6723f52f191034ea431dd1643f46d7e5eab4a70c24e64155bbfc601d2a418477a3f1c31bf820868d8ec4304590f314c718b653b2b9ba51ec05cdc24c2eb24230d9436614834280fcdbf825639c97b5b64fbe97666140c0af93e8b545ac947392c7ac46696837a40d778ed7b537886412d368e6e0e972445aaee687b9b65e65d3a26595044d2f1506590ea41a130ec3da0a26e46751d135e20b9223d8322166106259b74acb37511fd005671bb0a1a645e724824153ed049bcf1a3240cacc5093bbdc576b793fc4e1de07e218ebf9c2360ead5229980de3b538a506fba7cb800a7baaa2a2d6b4f643b00f16a750981437830e52b6156971529bfdd3f4f4b786282a01c026290f122e2c7bd5a28346c4244839d24984bff229827a85373cfcc6040b8fd77cc6a5416f2cbf1207929a32d1a8af016581e83320d069d329de3c51996f25956076ddd7b614ca54ff18174063f69061341572a1f8e75f37ecd688c50b23374343d25d7681f147e13b435bf6d3a8fc065095b2073cd9c6055d1c04c3661f0df1289b46d4a86c6781bc9a03c2af67eca358a4934035e69ab2f1f9ec96308ef5f5edc00800e7bae6a66f99fb83e7daf0e79f0cc723a7694a351377e3f635ecd964c8b4dfc77affb962a6fb259639a63ec6ab7375b51fa7ad323c3fd536d22bac32402a6c3568d6a293cc1152d2bf23c8d39c5b3e725a5802a44362a8004e5647d0adefddd4ca000356b2d9a9b4cd3132e0576631849eefbd604f631127e79125e3fb8418d462b0d43291f4b744b7b4de75a6ecfad2032bc3a700fe402083495e810c1ce3c353b24ae653e98e960d0cda92e3fac17057ada524b9c2e3a114effe020ba3ac501e0ed730771a4092fcf55fb3224256e61db17ac03ba2b887a677f5e1cceba4b414cd01516c79cd7508537271b3c12ec7904950d51bec2b6519e4e474c8d47c62080122c336c5bd0488cd48105d72df7625544b867f92ab955b9d9805a9a9e701f055d5d52cac47103a201c532505ca03578e9694a98b4de2f2f1363090b3c4f66ef19fa190dfb2737825242487aa4e666c755a85bc0df1e292c759b3338f6246d25e22a17381e8d78d2b18556f87ba26eca69e035cb5abe44306c016f6f06f770c8f28218d8445a272e17d52cf3dac5095c53726e636d303712a8c95fb149e26c6f4749210f85db25762f062b53a4971a2bf7cc0d41d21a6190eb45081fc8c94d28b7d27295e81b1618df86141bf05f61cc313065f05d365b266b2571929ad67b9659522e65acc05185b615455725580fad1ca052578f204f3295bf7a7ee0085e2d2872608ff64e0a472dd47c3f16770c8cc5477367f91a6775a3513e5669b3421e044a197e5c1265497be329e1546a1ed4b19928f0d89543b8b7af4b36b0d103484b972e436cd718ad0e1b5b8ea35e3fe86b0b677d458f10953bff6532b9360f37d16b2c761a59031ab4b20ec511cd3386e0317d21071c07be4f51067ff819406695414c114d93410e5ada7cdf097a224d7e501e80fe6a31663ac678baac5220847aab080da4af311a8a9769cc6e9e64da37fc268f87e472a2792864ff8c5f5c5c5a1277ecbc5e4d2fa72d5d338cda3967d1d41ea5f32c77126274709214aa16ba49901ef9134743ae81e2208595c1366f6c3a6ff0c5f15233decd4abaf77f688aa80e4d49358f2b30ffa1752b78d434a5dc9f211a2fdd2df0decd40f6a30b1684ee342317ccde5c59f30959b463e154242f22187178e9568583f735550bb8349c9e911dd43a0e129164c97275989b65a002ee53ceb2941cf06d85753b6424515f9b7019ade5171d750ea7498b7f7b3fab4743708a29d2702ab205531ab01017268a32047a189410f9b793332bf541228355e87e73c2d9708d438a5bea157700da55834254d4985b28aefa66c75d435b85b5632be283255d7b774856f7ffff07a314e93eb976cb6d103b081e2746d540e283170faa9fb1408963a4011fbd6f0c049e614483a78468914fcf6ec2cb3e3e9786571eff22cf00bebfd2651bb05f7ea1510a54fdf73325dc63390a04876e266bfd2e17a82beb5eb95bde4cbbd992665792bd57e4de5a2c98ed317e6b9a583a403712243e244e2e48c0573fb495955664413e3d96ae32244da5b11ab2f2de5bd90f1d6297efd83b67761b740ddd9e79b426e837740ac570aa4ab16260d53b3bdd7a72263d2629208401aa4797b2665dcd741a06308b563df6d9d7291933061e88a55805f27c4f216e45ea56812d5c39d04bcb4c3d24074cc050a0400f6a854fbecb525f59f86630bbbc425d3d35f8160c31a447798b351da7049e58f576696c6eae12341ad3707833e3dc68bd676f544c7f4d24e1e8fa4b691b5c2e710e557dbbf0ae4e3df14c4f83f9aa2cea0c9449508c08070679596ae026b5599181b23d84824855bac16649e54ee90ad855f746b22cbc405dfbf04a4a5e8119acd01c723d02b94e21c55f2f1aea574fa9eb5a7176c5646ee60426659ecae861c6c1910ab1a3514c1691ca61b0fb4507df3ea826ee8ce16fb27c9c0c36d9c60659d29571409b785c23c3bc5c4fa0ec0b7fad5b064ccf950cf9843a502f21b35f810e9248368aee27122611413ff6c44fb9ea5d559179285bbbc32177d6833e0da5b0071abe92b929e47184548dd74602eb072a457df54b25b4bf66041282251530fc1d245d3d35398021e92bb009e276289e2041b30463485ee5cb7c0dbd6571d8e492066c4cbb263d618446aa69e3719dbc5f6c16668210c5b3ba760320131b5d3b8e29dce41153bc095152a91d606124450f4969cf0331ff7d1a431362374ab493b2173ea6e1038aae9b15ee6203021a39910cb085a54b302ead695c44af0acc0b1309b96262712d6d5765a1a73a75d9934b6adf36a265b6b0b570644478336fb2e956c0a87d7ab826d30a96c11d6debb8191d75829413ccfb2d6717813567707bb23d0358740d1a41d268f7022b36e0b9e653aa590678d820f45563a40b75f72bfc734c8b6f47c7828e02ab8d1746bfaaf11ad8536a33966034090b8d62156e8aa524c7a08f61eb8fdf036976bd227af5f400fb7ab478cb666255cc35ba059a4bd65f657a9e2d4363f95bf9ad7f30d770025c0477644c9522093ec275752f54d4e10e7dbb9a793291ce0ca998746f2438e126fc50757b3f2da20323a6167b797d2c77ded3d5799f86a04124229c27ccd6492f2316130aaf17801e1e51fa133f4ac0074ac27e4e64769662e4af4033e6e60a359961db7e5340761624c38232a966441b959a451a74571b35bdb22c3636786f3879df9f5985306d08ef21704bf328562fa403740135421e4324188927e0440517d8b8237d3974ba24fefe57518670a0717f9cf02fc469f07dea3d245e33e7e451aeb6326a7f476e0b85f2827a1989e349159fda6c89a6ed6b107b077872986508b186d30958218422b4e2b25d6132ef2884a5d76c819bf07c5e99c700714ebc75831ed0646e317a3811b92a46e1a16a66aaefc95d9003127b7cc2272fa2606e4b06010b3a3a5ad555bebd5a79d3a6985d11ed523d7055ba35013dde6589111f56a9e4c84c25a3fc106ffb761470a7fa667f41871d1deaf133285191316921fb533aefce543b644f4bd26b363f29015d79c5afb9372244c014c2e3ad76113e8b56edad1b4244ea9352aea7760b8115565577e143655b847829ee89d37ca05de67e8c9dda088e343c2bbac85526bd95eb3cf3ac636418b68a1e54ea4251bac85526bd95eb3cf3ac636418b68a1e54ea4251bac85526bd95eb3cf3ac636418b68a1e54ea42518fd5fb08c697700b08db66338ed60c43c763a76f0f88d04b26da697eb07ab85536b399489ba53f2e1275513e209eac51ddb2df073f4113507a40f2597ceefa1a1959a6781d346e7468e951660049525e59b54630eb0e434d20ff860675fb2077e0403618f6773f0fa1eb120c3811163068b4982a545dcd3d631a0a64f6a37e2ae8b9e70d3ef3f455b03e646869daa73ff260771bf1cbb22070f1ea2b3ddafa66791f6d28dd44143ebc07662ad5cb1713e3d42a6b2268560d8c75600589c7b12df05ebb2350c28340f6c68230665cb57630cc7a58e6addd5f73ef436197c8182b8c42312db08c386b1cba771e1930ef1ef805f6410506970a4ca30142a70d8d29cc64517c189f930e0258c859821d5238ab8636405e3ed301512e825a3436452653d1a526cacf3e65b5defc3283bbff0b9b26fe185c02e5356d39841c8f38ca1c9ea111411fc0c1009e1c21687e2cfc59468f931f79ab804aa2b48f536014c40e575fd0458082797611c936625af5713a8fc9b057a7d585594c90f2622ee1c04d2f9ff774b6a1bf5bc604286cb09a22669ce67c1f6f528f2fcef09c116fa790232f054a73fd0c4a3ff0f27603da90241e0b89c81f6b5c54344b2f446b0db30d2c2334ba5868d973027ac6cf7a9ff4011dffbfdb7226a7601d4f3ac053f42a9814c7a29b62b2cab62e5e5d2c24579b233edd0ebf397c6a30139f612c6686b007465be5852cbeb4ac186c6a30239f612c6686b007465be5852cbeb4ac185c6a30339f612c6686b007465be5852cbeb4ac187a518f469aeb516543cdd948811b25773717bd196a518f569aeb516543cdd948811b25773717bd19de7d497cb496be561ea39f10e9d05e610005ae3c4d70fe64012a212fd4a62b0b7299b045f5a72222b6bb525e7731ee76fc01915497d8e231a46f024ff4ee2958ea973b10edb452602620d44a22b87e088a413170a730af337ab8f43a599798590837e9706236b762c94a4a751638933a4faef475437e30189694a2038fd09a2d6b7a50646f96671782820d272659510d6403174dc761075be065bf748aed52695753b30855352c6797ce347ab7f77309beaaaf783ea9d824b8200413db4fa623b8649554cd5597730c4f4545721b402c8205312aa3f4b46418f0a65b9fb1a11ff342b345718b5d03c732995cd8566d083e102c19d98b355ad56fae3e34ea9267c562d7094c09fe43361d1f0b7fac9c6d45f7c83d8563e7361e77dd3c3e029764875d2c66c55df35d202c1c4771b35d6045d12d73d0066d4607625b21bb6cd80062e4e61abbb66e1342879875e83b290ad5b8d728aef7ab1b55309d58d31c56506d1b262184a83f2fe22a224a6981da1b008ec12ed5307a563f3c9a66491dbf7749b3cb715559036248fc996a3461d2127e23962315a88b453deb910b9640fa485a8966363679e6267282fe748b14d03b7d3b9765ff59b3290af9e95a305cc7479de460046ed13c1af9c441790716c0256fd3e95bbf0f4f041e829b657d0a9421b7b2b359edbf3a79e72f3b308517c77af4e1751b2169f7219435a84699b91f5976c186235b70b607223bf12f3d082664e48c024caccd3618fc150604deff2a34c41a696035cdb11b76aa930785f40032204bb21d15130845db22dc5d5bb68039a209ff462599bc34c4d57904b5134f6b6231d34076f17745880aa3447381e8733612f506b852c6541710184b6dc114676fde8a45b8bdc81efa98752f063621360b75a6630abcb97e5c0983775ac3541875c6967c6e11f1157948ce0a0d46a42d2b204a6239650019b1021360af278823dbcb6f66d7138b3be6bc746081dd290901663f1ba1017c54d941de14e34f4e3c841f9c3ea9fa06182e4cf06507f7655c51fdf520ed9bbc529e4e7a78e04f266f998f6c1d34e79b1f92a1a908df19284279ad04299f8c085d25d2a6609b00dc65817b843e5cd0a911d65cc737b368493ecbc74c76322a414bc3ceb63bde59b94bd4e3d54816190f2eec4270409a5d07763f95df30d955e260a35824322599800c2bc6a56958df96332307167d96b3ed29cf1c140c3c3c2e1271dd9104ef76782553c5d53a10bc0211e7cd65091210e16bca7740472604225859d7903e44f7d655ad116476a225ef7d96431f4e1dc65255d7b92c22ffb48104eacb270f26654c29e177045b3d5209625e819a37e48eea1eab6cba0ed875084cac7b2b2198aca45c6ad0e34b2aea2327704c614983b264239c1139188c10763e323d42781042ac66eccb496396ee9808d3929d0f2b2d212ec9d171400bb2352d7deae26d09f78220173e6d19ee25c3472889f872f253680959f79f25d5e663387e245e68a7da947eb251dc0f987d2b0aed054e2867055d60de2f9d2a48afa133d20f927ecc4a4237f2966115d890ce42b57d66592c748b5702c84e0c44da1f7197f4931abd482b59b8c3116380898a7d0c65e026a767a13b64e25c37afa8867e506b467520abca3ebbd47247a7c5276b9ffd075db956b0742dcc1370cfe9ed15e6782775ff9f4f5be903b372a9febf04d35a0152399ebe7ce010fa03b573b62930a1fe31a50db24b1027055d4a3de64a24acdb7eda1e71659cd697695776f96e9792b034c3069a12df51d853e070fa7c64bb0a1a1452b11606d4d709799a853d5fddfd533583656fd1e15807dbb9d2333813ca186ef0c448f6315529f74f3b7d713ccf29b56b0217fdb8534596615d23c5fdce61773cea764fbb1349d41a1165bbd8134a459c212f2dd32c7c94743307a60d7c51137973661e536805dfa13c6e7305c36108d75c5a737a3e171dd07759020ae10a3df97528987a1d6fd3d2cc754e8aa20642c3b11fa65abc7e4983a71e8f32000f1fe0543656fcb6709b348b189bbf1c6f77738322646b814a520c307130633c4d824630361217c8188f4ae44bcc291c52aa916a3785d125426cbee032a89e211c566aa64d9df1053664b3e4118e49c42030c51c44486bae0a1019136a36672e3cac6b995203e8714eae07741bf7b32656e6f3a847edb1b661496f9c75393a424a97675d3261723804a5db0c3079ddee1fa9e1067192bb224155689b524c5e012f79f9f90b355db0003e769f7be1bb8b7aa0211e34a3af0a2287b9e32f4fd2d9079b474e788e491176b42d491c70bc96013bde6809970b9529ddbd6f59f5bab82be568a213f650d96555da3f0f282d3b75eda59364045f753653aa563bb431fe615eac1023dfa166455534201aebda5e60d34654197ea83d343f39cc4a8d4da749005ea944b2c5a07ce456982c6ca8ba0075c5ca651dc37f48eca7b250285f816659f7877dc97867431b50a179b72ea82937c94b25ec94e33e2944de3d8c78fd1602630f20b06f5b597d9ea415ee53c04a59e5561d42b18761f07e7915ea109f749dbc7e268099f23139a2ae589213e66dd1dac63a9c1a1121cc9a534ac5150f70c7cea57191b58d7da008c177dd021d2b43f03b4e86fd0a0496bd604a10e0884965c1866ff75a6a767a8df45b7b16ea066c774d2a0f534a49b25e1628dd88733387340211bab608761b8414672f2cdc7b4873537baca8cb0380dd500192c5420b9be4681cc5365d545eafad4a24ad9921986c9a55a0a2a851a747e730234cfe127eefcb761166cb70cc9bd354208fa931ab2cb90e632b55516f51b7329aeea7741d5682578797164450c83809708334361bcfa64307f21c2fd41a67220e80ff6e93d4e05cdb11072d7494512010eef6090eded91d01c94b2100b4a64c42d858737b48f61ffab34e03f225ee59d4ff4b2d2dc74e5950439c090b513c4e6b87d608c9b7782f426aec53c07e6c1560a4271fd505da6498426341f2a9fa3bc5c29264e8c58e1b5eba1b72b5b63d36e59cc3355a61e4296f763153398e842a9d68fb755ac9b7422965701b7a14716fbca8f62951c09a4df5a6005e52237a6391d12028c1cd990eff74e84f8418206d7e189e71bb71887497c9251990b33965ad26f647bb11cf139e9e7547c7a83479a81ac6368fe0e81c65c99105eb6bf322e62dce2fc14f781baa254e1b1f45995aa7bd895584f84364810e42469f6ec70fe50ced37c3e4f84b49134f7c74066757ab33404c7ef1350bf2ae925c4d1f2472ba006362a2ecbb405357a439e5730f24815d4e39baa975484c65f86a95576904718094793f186364c06cb006a7325e053b621e6a40471d2f0e75ac12a6b2ab14a1a3197128f2fb42e480077bef77852c4b66e567ea05502ebaa8182191f8ea39d8c8830e87024773a9a11b3f468bf356dfeff6618eca5475f21e664a33593c683316dd1ea8ae0f5db71ec53236fc94365079e459ff7b901c4cc4492e928cc9275a1157716738522dd1f84f36f135196c58ef4d5a4d6a0253c5d1132b1fd7fc01481be9259c5723370cbfca0897090b08b07aad4e79ab0b47a97f0010a8dfd36b62290d38d03e0c40baed921f5939ea6f67b72d40f9b6152db67b06420cc81e682edf5773cd128d023cffeb7ef12c592d285e302b3a8290343980e078e183c850b84cec6b5407ca6c68092f67e8ea6f41c1846a515d9e9104748246578d85a7339ca4cf5eaa59a90f7802ab7179cd9665d801de4368883f0910a1f068cb10f546ded553540405086d3c9e2877ab65f03b80e79c0d889fca4c82ca1055a218ec2cadd3d72bfac1ee0b360d7055a9be03734dfa9a76ccddc57051f4554dcbe3b237569e381fb81c16399c524c1f0bc75c544bbd244d0652711ba2b12c6e468c7f14d50f0d62b3048e69e52c9d64cb2cba237f2a3853cc382b384a1a442f1a026a35d9ccc426b54dc84226ec301a61b0802970c11b1b3bbcce113132a84e759e231f3f7cdc1e8e6c703a6c8bc468d6aaee39bc8f7d348c89b571facd8c06cd863519199ab926f7d3fc2d5c5f904cd0769c77f0f67561d2bdb35d6680dd25dd1ab968058ef6784c86954a90362504fb559016e29dce1ee6b3e15c5c88c80d372aea7ec5364d0c66cd502edc910971ab5d9d7cc0b2cf61b663f76ee7ca4627c9dc2472e731067dffdebd6d2b51541a8a0575235298f870c90c977acdbca50aae696906d34d7754bad67925f5eba42497795b3515632c62129da2532c0d582156f4033c8bd8e64f7182ad5461b8e92e21504c13f3e9473552cfca6f0df1fc6ea2a9fd7267fffc163d6939007bd542266781096cd238210db2c70c37be3499030c96c8588bd78615a7beca1ffd3f920d3db0343f0ce3661dee2026217705bd6b97be5948b158137117cb0417dabf9d6b8a97974b25104a29f85bd96753eecb3b989ae220edb05538c85c71357c0e1f33fecad34dc52f8b38c1149145690755663ebeca6e0c44491c5aebb469c64251229d3f61142e2c2a7675e5e0407911d11877c006416c05787edcbb0074a6bb311657a0c255f37857504a16bb1d16c68800789c841bb47d2c16d3d10f2143833f197a2a1255577d421b87c34f7dcbb93464e205aa66972050587184a735ba30750746091444017e9f634eb674537f681f7c1829f62c307db60b6fca1f5eb965be71f30618173e4f0003c36a862a2650923165df1a6624ab2d2864f62b63bbac5e1de6dd935b1415ab7e60da6b4d047d9667065f063e92b3f52e0490be4efde45561ea5b985dcf7bee2d6203f5384cc07d04a6c6b21734a9712bd3f0475f48e0383fe3bfcf351d64863b29fe62706ea43353f0727301956edc114a4eb06771caab5708cca478cb4d930cf1383333535a9a3e769b58110521da5df3e0b838987ce902b45183370015015d2274286f9d3ef635cd9d7f3bd7321045583a143bce6f05770bca5c158db4c57a8173555ca8256b742ff69028fcf88736760a865fc7c0b46ece782f14c4f94446a0e16a5752ec6e20f3dfed01335005018edbc95adfc5484ebcbf8c019db82101e8f91d24586fbc3734dfc134b8c5f907d192805445f2b110016db660087f18480aebc4297fbba940127db60e087ad3641839f971b26d93095c40fc436ebbb806a4a9f92bca69f65f61f2f852ca06a95d3372a070af3fbf41bbf2df3e59ec465a5bf82a05220b6f43d965b07119424976883cd6430bf2e678137d835ce3fd6861830f270ee6e01d1d10e9e4024b8d2b6983b76914cbdabd3f794eca53139802542d5d320de8ec0e662256be57ee014e5438994e231634030cb8cf455a9a841f6786c20555c04c5f194f39655b8dfa74560c0b836376d1d47cade30a49b4368d6a70a9ee0c44dc6a785a3bb13123206e1f10a9050a06d8b579c42a9f06ef8b2975872f9e340d70900aff84f35ec974710b0a5d5c2687f89d436e5a3f0512f04944a2bc311f5d527b4b5b8db949863ffd184c98df5fd4bd4b2d03205547314a074fe18d2717dea8f76f6b77b937f8039660a9e73a09314e4b7dc7c6f620b276b7327b84524876a5554e544ef265b5ae8538a7574a16fdf44f112175d63f04f9571ccc88316e5a9b1b1d46ca0209a6524e6c2b262e7a5ceb0f3b7674091034ffde6dd86d3a6f41608456677b2707d8fcb91e67a6e3510a55aa752583b07974520c74d16b086f0ed57c2a3106ca4caf9a853488e7c656cb282a3015c4e91e9b4d6874f714bd6a0bda465d68908c75df051470e377cf0fea3dfd72710fe75f54d79a16297bc45310cf69697cdf4f154a3b426f8c73a3464aee97732d16170670090742ed700227442b6a76e98a02749ac1fe140df17925b9762e6ec41b43556dd7257cf1fbf2168e2cce254077266d369aab3ed9a28b5b7e75142c766c3823df1bac536a81fd793a28965e1f3a510e363c212dfe21400c994fbc5becf2ae2e52df0c3569430b57e0c4703e25e288083e4cf44631202b3d2f406f1c6c86172146363a1879a6f02252aa7d1ac6bfd65922706f524aefa415ed322935b98c8376f7e6b12ee5a7673e9beb6360cc58c53b17047b66dffb7c25a4003c2b20333d71e8134f0694bad2076c17b04e11f87d70c0692b678d6c6764ec607c432226db5e84a501443b99e5278b605a55e8a9ea7670b8755e66bac8649662d37e70c0d77142cb07213ad2724a5b4a230bd6270f49af3a4f50b4e1593098cd9c766bfb8c690bc4841f9febc6422616bc73c029fa360204db4db3242a303cdadb3353e2134b93af46516ac3d86966bc515d282148751970a96bbe2d604b872cc1646695bb0a9b7cc53afcf563291e1599151b1fa83f3e0b82741841c15b4943fe2dc26021471a01e47934d71043bed51d0216e6c978272d4113dbdffd7ea0b9157a3e9a5c0796570e429948f4447317cd587daf42621ab11a752b554f7e26f97648bd29e100e2723e3414a98c5c2b86eb01380862542be5ca07fabb4b22c20101322790b605ac80c07775e1b54637b31b79b84dcd43a3c72b5970cd1156f779c15eff091b76efc609600a28b43ec914ab09870bab0aada8c300dd5362029d1e4602e72ed35ffee2a52dca1d86227b4bba36bc48762d51670e37667241123a5464010c8e3344c670a6246991b0142fb3c3682a85dd2af7b79b65b222c73356bcc54b63b1442d2d0f8022e81c5a575da1a702e167941fc7710c56435ffd0e84022253d924cb1414c3f425c7aca3506ac45f63b729383e640a431e60004c225791de39dfc93c49a8f6174211703757b224bd79d414f4200c730c10dd1415643f5a32589cbc6e4395d41f37aed80a25ba71a35be401244caa917e6864143066c18f9c544a206c627180385a0c7ede3182f2a05398c08e01dfe4db20870b884bdffd303a6269e14d1732a064358d8a425b0fc13d242810111f4d42682735ce0d1f34a676a7d0f1058f590630e70506641e1412328721ed2161bf2639211f6c56bee1a669837e8005cc0273657be3484dec85ff4674ab12707d5737543592cb405f210a4f89b9c45ec3757b0cdccf277568adcf4f69078b203dd27908ae26117d94875f34ae9a0e50e79aa61194bb0d0a3d743d1ce7df890317d120635c0eec4005621a1445dff44b3908054e79cc867e6c294e6cd02bcd514b6c29171e6fc3711e96f351ab4ed146b0f78406253e6c2a04d65d545a66e20fa669b37d99c96607e1f8ff62d7e9a1246ce8aa0fe2eb956563e0e170a7274d21f13e1021b98a301153f6633bda240f03c5dfbc7326636a2149dee30afeed4549b66bab7064f6bd3053c6761425637e3444fbe14b4e24821a2fb34d450effea01a20938678a7da109dfcd5f4ca72ea32099465c32867be424ed9b862da228455f54d0ed4681e4392c551b1b40f288656daa1c9a61edcffb5ec2358a2f7683e65613c1ed466576273ebd05597eff0e6a3eb6c24767ea82e362ed66395c0ec73b39e9c0d1430b53f33ff06f6b65ecc49576e1dea67d7ab9a17846dbb63e18a9b20e9b03b047c6b9b63d17dc2a21eebaa053e1e71a0ac8a3ac54aca5251a7f1869311777c653a72b2c4c57061b1b18390e1d882f51319d9d75041513e57b7e87b2628b67a43166fc220266a73e167577c55ed9a90e613326b25e14ed920c23eeea2c093c077dd5a6e651dfc2ad63fa307f17360f5457b699f50c49d4af45073cb146e04281154289934edb5a5a5b22de9e19219b953c8d72672b0247c353a522d916499246758f3655252bc4f60459ca824c3c8df2762410975f99c3cc49c051730cadfca55c5034533db63b2207a42476410d9e34118bfa486be5e888681e87c412a1cd9471966ed476c063f97ab1b70e4aea1d825b38158e75bf47c4221bba0b225c905f378e5a543d7726ca2a63f9432084aec27080121d05a7793171bc219c586cf7836f589eb220a094f9419693ac00eff01a1a98dfdd1f64f1466d79ab8851b8a94b4429da1a3fe74565353ec1d82c141fa05328e1cc7b433c474849afa67b2e2b014e22cbaa21e28e4f71a48f6e4ca6bd1213d205d52c2c88cf25b0c8e02dc9a4c770378c68472574754ffdc0be1ba66ae22d3c27fb6ed219a360f108db51d0c130225618de1f0a7dad5fa8353c0b4175540cb90d593c80c50646e056760bb96cf27ed2afba3c7418ed3a0af38102df7029478e356b6994874c4f60c4765ce186dc281bd86b6126a1ba0f77d18b5a58e59721e9a9834b48415a44775a91379554ca59fbfd5b4bdd32765d2addd41244b6e906afb56a3b4ebbfa2282698a0ac65f446537bb550e310b7f49d7c6a61390f47711285e95026994ae672c7c9369712c376ffdc73b37aaf2a45234647c4ed591e1347a66764cdf5a465a2f504c3768c79c480bf89b0963d31f6b37ff12387bae255324bd6b79a9b17f566a94fa7a4ab47341cb8da21bb8546513d98cc57c7189756ad62e982b8017d876f0c5ea2ab1be974eec84307dbfee97657e348769a447837ac311f43a81ef020a7096e148120f15211dd88443ac6e8d7d6674ca333c80a66311eb505f8f99297809466606443e914664394e5f2d186841899a7f781413ff5ba20f9567e7c5562f46629f08bc1a4208d1b3b405f904d150e24861005e680903b4680567bebf0c52d3f2d2097c365a796028dc370dc77161b148686899060e3fde807f44e9b8a5369247e7715b58c420170f0e6c63ef77195cd5a31bf114c957cd18037122fd6b0a2ff3d12d911a847bcade606d802987182ceee25ab20367574a282d338866357b95a18f54ba8ae6470f31895be87eaf035c550c7afcdc973f08e2b749f161e32ae27ece3adc81007e368c17090546be0e80a32246f5a2e725a41dc82981437454514eca575884e26dd9f8395e7329ac042a718843949bf97b6178277b0873c548f3a69d74676a974514a8955305a4544d50e33a406ed662351b342450e583e958824b4f6f452cef1cb943d82c98e81812b5f230725122d5603eafb97602e4df479e35321a872cb126419b5a697c5e7d5fbca80f6c45b5820b8354333bacc31a0fb0bff21c498268319eed3f215124235e2a30ae7c04be415fa067fa2dd35ce751849aa209c6c7c33a1daf363e1146506c9287dc0525c74239445f24181367b835ae5c7f27b331895bd1cc5d5eae9d182fcbb7333870f526448e44de41863f5650dca95b7a93178736e901eb494a2e5a014a8ff30411e8146f9c86ec286be57553e17b6e4c831c6e36cb864461e760ea343e25fb0fe441f809f0c12d7dbf1ecf560695ca63c674df1d7257f875349c1c3eee3d2c5aea79d60393168d0b390fdd17230b950bb41b617086fb96543767846b207ecc58a10b8c1e10a4a721d1505b08227cd267e809da6cc547f700cd266339eb33a71e35329f254e24bc05b948b616a8eb984705499a13e9ef602f65fe2f529ef1dc6544ab9e46da47c26dc5a207018ec0a22f490bf829e5d2c15369992f2c8471970a3d323756e07f331566dfdb7e3d541d22d51d093e8d47a1722f5ef55061a4d244e7990716ea2f4e21f753e648205a0f17c01c026adaea2b5aefbead3ab0364c717e694b26d8c3ca282ed7040441b3ce14083bdd18f4f8952b336c08576d88ff275969a74a6c9d594f8629624166f7db14f0755e10bf92907882bb575379145f1ec242f40844ebd14bdfdf236c854f295e8caa42697eea0f00827a697981b4327681cab82ac5e3ba7a56854261dd778d06af0f9c3c50724b0affa30b4876327060fc7c4c2cd4aaa8199d3475720116f6105290ce11f73a906c53b5941588a6bb46cbe32f174254373fdcc4340cdfebec16615c661dd77757730ae9af0ae797035e0891bd2869fcf700a80eb43eca41ae6e3845912208a88836a11f806b04ee4c62f074fc52a1324d446981d57651e6a22102c86e0343f6b510e0e8127951d0972bb06d1458af59eb5e53c1b271c2b5c45b2366c42ff3df88365780fd008d3c2c56538e6e37e64d785c72acab12d8a979614debe645cd1b7b09be11383c1a8c9e3c906dd61eab56cc728b8d1e2c2abedf7bc5587764c18eef066c0bad4589cc495e44c05e0178b86c0126d95328b6b6956ec5e29351e3c8da55029da053a7f3f51373d3da485e248e50be3e03731e4e013510a07030ef9f25488723283ac1966250c418e44bfa1844301beb4248679d621c3145633387029008253ca52a44723a3172270c5ca941ba29f1ec307749b3730250201127c8963d2848752a33f6f9667171e1d928d9938104514f0834b41080405d9c07533d8009795f44c3452722112a35413c649d56d16eeab0ba6f4373c9775f6e0b5a056ffb1ee507222908a50b426f2d790b3eb0b12055bb8f6d5d45423e6e13df1bef76805af9befe0d6feb412a0a1f4e766028f32e4064cf148e3cae62ba99a46c57bf017bd4f776723cd9452784eea318c3925f757674db643485e4164019375a7766a16c49dd242760b92d47b8697676af17a43c1055305ca125ca5fdc3d6d1c24c4064339115b050a4ba7489170701241a77927da73eb64a944cd4a101b6b56b3a2d049ecfa9f049a9d955be2b9db37382d3d3a0021714b3d384814524ab227b7155669b6d1e843223e1f1741b4d339594d404dacb8c701aedebe117a5069233355b243e5dc5066e3b68c6ab4bc2174475f670db0f0380db716c2781e3f2072ebca9b594d3d843e446fbe5431433d5441b2595a2954d24ff3b9a64c7a4d674362c91e4456fa3d62b72a387d111b2b0a0c30355b82036d4cc85e4b12ca3e325e373cdc5a0078c33ab416585d45a70c30e534752b03f3d31b8e390627952d633de0779c3a35417968cd513d080969f562253fe72625ef6a612f98361969811507b871ec62b0c4b93dba6d1f5535281e04f3b1cc7cdeef9e56e26fc03afec5da3eb7d3bf61ccda0e04423e3c58dfbb88105cca41537d5a2f07ed0d304184c1fa57c95d423197b0174a04b0d95352eb401c827dbb13ec37d33562143f1d1bc92949edcc7e0161b0204c32c7192acd87b14d2cff781037bb9b6e48f6844b599e50068d6dd22dfc59f03549969e0bdd970e2a83695e01b04bca717ad7b25792f87c3855af0400313ff6506abdee122910e077e08b025c6938a16133037736e7bc8c4d4f8fb36d56bbf24492c36c56493d2b5e691b80331591e9142b07760635b045690028883f16e8862ebfcc137d9326bc661d983936040920114bceff13c60bc956161b93778a04c8282d4d5b4ea1eb230175a54f60f0a78740bedb1b3dec20aa19ee60a30d72de0e6b72a08c2ba7cc520a9da94c1a85b4ee35f4dd917531232f49b4330a431b20f665bec9fb00ccbbbe70ead9fb45d2b7616df854cb35b831f2569e677f0ab94c79659459f44be17da178cfaba24cdcfa225ea21cc977e4d58d72cff3b05fb8c802070c2b236637eab50a28a40c516e352223b6b7964c7c805271d322835c251c0c0049b6f5465353bc60ede3a707529199560962e349c845956bb21f8b35e27ff230886b0c28a80158525c18aa4495e198047f1eee49d14cdc4890bbf57273e2a5361e243a664449e1368bc9853d8cab0a6da0e9ae75a9b8d83984fbfb277a61c61fe470dd512fcd074ea3f03e532d480271f85b903bb3e9425df230842e9edbd624d294a355cb43b2532e2327787ae8cd62b65c037de9aff25844509f165af9a23321c7a230fc782622b7a2547b6ca56b42758ec81984d05d68cb50231e3bb62223c7119d0354de905acc798a57cdff6064d2c0fb57ec6e343943d5a74d73663a12bc8e6d093340fc631d520d6e59fa6f55f162a35210fe2f2a0ddfc04f36dea84cb698df047ba6f44067989312da02434734719f18fa050b676ec10e259384d92e846bfd5142cc8c5a84239023fb2c5571037bb22723c17844404c2f4da0b0704bcc04c65e094c1341754f2f67b3de2c4b28089b04308f86256e225106d36aaf4f5e92ca519d7ce65cfc8a7466414e7d0989674806fc022905614b5831a1540b67b442cb3a46cff31f943f814236e3b6343e9e4f1a335207187c238e6849da173cf609764b45de23010939992465532a3a832f846b566ba23530f7e11d584744757992260a6fb42717a1f790330a5fff120066715fd32b3e06dc45043b15332c1ed03804272255a14902e0db5ada2daa3cb687115de6fa7056263d67603c87061d828b0834cb0913550766ff742c540935b1219e4f9c20c118c2da3c5907b7a63879ac655347d4867aa0717e0d42deb80b88479548ad68081104189035d1296f0594707e3276328c19e4dc4527531ff82646812c39a00a76718c201c3df5b0815831c7e336ac7ae53b68d0267d8328093599b2965e84ae814a084a3b01703be30317767e56a41c6d005cd97e5ef522384ad009e95e7f2735299681bc4ad7f5e02c58dafb6654588c32b9696a5079d9ec0495b66667ccfd895cfe23f673900b592dde4b7c1ced74e319c6a1b12baa434d4a06e2ad5523266a28d607d070a75892295332601e6badaa612ddc9575d63afd4071606f1e3a16163bbf10891249cc14775e616557a2b6042a15af8a4e2bda737a0fa7ce0dbf6880126aacc9542af6255601f3ae35437f536e5b30a14e623e9457a0c2202e6aca722d0b2f1d62ec45b2522e17c32f0e0aa91724621e76c17edd7517ba4d559406ae451cb0255b4b96d444cee08d452daf170646aa525205fc054f5154c000cce9036f4efb4566d10634232e58d63e23e61b1a035a033700b2f576877b3a3b487811755f862d56e412610aa86fa75ad00f9778302d67367cacb02cd9c02419e0702c41b7890f4a21f5b15899b9722d8e719825472b3472759b8c6a2349d802733dbc6b8544816680d5a1284463ed46a8a06c1848d73a5152778f4525128258068c343d88b245095f90660fb41b620021570e0e3d2b6131723aba362198a116bd6ced34bbf70a2341aff27e4918d666d3bfc9099abc567ca0054b4d0ab80164788b5d375fc5aa580ff9887e5f105f3e089ff0438c759f67c4679c41ef67f40934b6615cc9ca920cc4f9e83a2e54fe35e43acb3d97b9df15f50ba24ea5dbd551f5b60826187d893031812255811fe333f7be8d18deb7182a2fbe165b9448fe51217df0042deaeb0915b09d6c6010223c7fd0521b6c529f3402e7cd4fd7bc3048a32873702c31854257be03735904044b1017263ea066fa0b6ef357743f0f0f5d1ef3e164beba734e23a931661225345878d22a414069633016342e546f0c5f41996fef335d9c842202ef2356330db8581da0e2697c87797d2b8ae7484afc342cd21a252d31c8143caa0f5822d30c2e325e5249357f038612b92f8a19b00dfe0afd17a0200f760e2cbf98742c2f198725e3d6770eeeafd51d8eda6401e09f6f1db4db3d0d8193892b86094a6b22b37b68ddc8c6268c71fd675a2d9d50f45e3c73c0a0c21e9b8d1833f16d1f1745d91b7573d4b458a055813889c8ff5dfa547b6bf2483548e939230f2f3ad85fa3d41d1608773e671ea6f211849fc341bbe056481d3c1967911d34139cb2cb5c6fae2a363fa76c31cf1fd9543eab026903ffc932b657545aea0a246fe1ff131113ef1856ce888229acf68560ef312c3af042014395c1f556aecdef191dab09161ac6f84016b14a5c2970f8771ea651505e35905ab519f019e0406b507955ed7a0a7cb76325d4c84d1db8cc615fc7c904e09e573b85d8dc2f3fc017181529f235ac43b778c907515ad0ec372d4941c23a97c8fd0c29a04029cb08c125986abb67c932cf668b57c827bde7817e4ed4580c9432ac11ea34d17a934eb96a0297b014d79d6c5d353b7b2f3539cd6dce6c8f496270c848248d055ce0af5253c96d852cd7ca0352c0cf427e6529c15491075f6992857240d6aff019acd6797a9dd32166b9b2c67ebd864d2bc5ccd43ee8a7786edbf42d13a165094f0443e545cc926c3f0fb4ce714045ad0597cefe788892321a4744bc0065ef7a1397ca2f14699e7862eb516d522886932d710f7865a7535b6ada50d40c32599b3069727a115a66e6747de72c5c04dc8c271446c70411c29f68d6256222215a0765054804348eb14c4a7258645c3febb81d17999d4c9d1cb840863caa2cd290fb1ff660405ae647836f0c782e60ad1d757b98cebd5756ea11391c23e66f73167539a53ee17ada50f169e9f4610e3068617ee38da81fb6e014766e481640c2e1a7083fc34217a31dd7023417ae3ab2db15707ffb7534be22d6363899b96201159d183613a52ddc58ed155288c1225b5ca47834c65711f880fb0a250f0929d7374002269dd93ffa2e9f430378e1277470a079d701883b0ff261563ad23d4b19796955deb8ca0c1895153f37c342058d3da20c3515933cab0fbd3e991f386637ec786af418415a78272d45e8221a25d18ec670af74b77d2b8b862db6c6a1233a639b51f089b7511ecce40665d60038616c80599f33f61f171b9b109e8b27321f992569d74b4c082710a43442b9145d16302a607c0ebe720d6d7c44fa44350e0a228f012fd17874ce400d08a06bdf00a4f9604bd37027489186a301e9995238cea52c6f8e3b51223e73283ed051e726c71c746e31b8144669ff5574dcdb8145ba1707375ce8006df11cc35454bec91228d6bb264780540fa4e4d24e4826d07defeb353a99ec5d5328c23c0eb7d2bb7a2d64d44970faae6a9d2e5d3fd5391d1cb4a7dd7b7a86f656e7a5af4b179dd83402745e219d036271e7f66f76163ac751bd337c24c4aa912f18f79e7be3e6e635e731b46ff02d987b53ebde70c7debf39684ec63cb55f8168d24a9d32f7ef957de4b1072600cd7b00245a656495c51a20fcba573777211c47dc378027ba1a4f6a063c0537dd6edf7abe518f4055e47a1ecaed2957c7558a3c5e9b763fcecf85799030746a6d1c173ad7ab5c11336d915bbdede93bd66cef6c4040076c506e4a5671f51e6339f43c68faf41b2d61883d133972a644ac2c2e48b6c974732d1c3375e75cfb1d5d19f4654b74727e8060211c023d32783bc6420d4164846e9ca0f63ca75d053d7e28bd1e691bbd6d25b6b10bbdf76060027d70592e3a1541c51e1a0a47fb716604fa4e499bb4e21dcedf7211ce7ba957ffc60576cfc2715101bde5198437e45b2b4ba833fd8db42d09a01c24a687d00c98585b0cc8748113118bed1a8daf722ae66e212f27703d561e9e532279b1184d0fb30c5747e27d4581f11c292f184872405b293a42a45f2b66a4c94151dca52b354f0f6474af482fc1ab170f827d94364164574d92d3bc1d1f33ba66a223710a47717e60ea9c2d2b1f91b9034ad36d7653720553fa1a5a4c12063668ee24d701486dcc2f2d435c2046558346c67ac92627c7502fb9acaf66da19636be2dd1b7627d67f63b65c322b02adc57be16a2370c4528c3d59e5041ac844ab7d6a7deb1c8208e96d0ce63b0aa35fc60d9398e424707c601961b69d5a647abb6526f3ed4d9738b046b81c1667853a185acdfb9d7eca9e585481a3aa19650e421cc61f5d5c6b78211a4b68fa5cc1adde18a9f5675e933ab53835fb007195d18025eac90e1d00344f0842852b3fba4c0233a95ce27d5a857349e11194404387cc62cb87bc2f7e31e84501190b4d7b6b9b78c23d680d9ac77d34497e24615c74eb0ccc4ae92e1c25d33578c3583fe8b8ba04d5f7e129e2a7ad7d3f5815090e4cbc5419f4422c97ea1925275dbe349a77304048d74b05d849e531160cb901dc47272b2e461355bbaba21602bd0420c8edcc21b244344bc2a7985c255ede62ee9e764aaf5f813cf129bc3021e3815130361a67535c1c375c3af32da4e0515ecd3b3722cb0ef414b6e0ae04c50d392d8738e145b46b232e9d2b6f2b1b137c1f40548102753b403ed34b0d7c5db0da72458a884922736a741deaca0bf402c46dd29cd152fd985e1aad58132fe4fcb937c2be7743e76dd103f124f233d1a85243398a7e08655a7a4c83ba8073f04d1310c22e3456980990326652234935d2466e52523e449b57f6232ae1327df77a9402f1387568cf39f16d907dc51e3605db4100952d657ae9a45d79ce165805df3d357c626e587f2bff72e9ae8f43843010097532692828c309347299ff41c976b021c4d9fd529045d775ddd5485f991a7f7c61b92e03b313ff3f8be45c677b81ea33a38d596f6fc8de4337adb727f1776561cd69885104522a1fee8fa200a87c3426a13c354a2711177c88e3e6268ae7867b1067a162aa710534564fa931f62dae6c5e601b558abc610ac331c0246e3b7c298e0bdf3d2b088738809ee803a31c6265bb35f078cfa35e228216de43b707301a9d33ca359f399a1dd4196419f847c94f01dc9b4add299b1b1d947f3c2317d822a77e8a4bcce9b635756b07725d20b105abe6d629fdd3ff3dc542ca4acf54e72fef031f3c4a2c2810379e090cc04d4726557ccd735b3c3f7c95db1f7982f48543e4be8904af68a23188216f58e5061270dcc50515c07ac5292df72e5feaadb91cac944e1481b1573f330734280786f902ba33032efdeeae516777832da6ee3b47f29ed4785ba8b362be7aaa0624037529e711113d08156b7de9d18d7d76c0ad01dec7c84adaa6e406832fb069764b116a43cdb26cb3991a6086d21968acd03942b4872c2f51b31c63d06fcd1e45d5da31e830581a2aa208540f04ff56b7b6b80cad636a560354200e3910824c03579073f9845866b90b3b753e565d48ad194e59e75ec439b1c9932ae0e83178b14dd808d95017709966ce1d582a6221e7c82a71a578485f1b0ae9382351fc049400a23d61ebf87a713e13243270c229e992d406b2025634f5b7bd0508e5b001074851732a8c3233a6a3d17b78c13e439aaffc11e6a872040376bc21e83132757f5d4f4cd39206527d297f648827fc064673a254d30b064c98e1be36aeb5ad2fd10bf92707162e1621225122439b884c59f662303982e2000644c136c495711d31c9162cc91eac0a726d264fd4a87c48d5fbc1023f55b749bdf3a60bb946250cd5675b7d613c64724d07d919d25d9c703f29c66895f58b235ceac75a98273617b015e72ff80cd0216e075f17a6998640adbbae668a9a313dd4931c764af3f930278d52566e97e6733e1ca32e9dcc4c46a0ac223bebebe3136296240dd188e70ce538f04691ed5907fe9fa11dd8219b5a607cde5f811f3911563d8555df51116b07f5945062e6ad5b67fcdb2711dd532d67a97a4533cd0a7cd43a391b30339d0db77e346a62283348d50fc318002e0b651922337dba50d25dd15cec0515eb780c8dc6280d0961a24df0c72d01a552461339faba5ed9f26c145ceb0865ab3bf41107bb815b31520a60ccddfe16e4e15e2623167519f22f7a11eb3b85146494166393e00000bfa51f7c54ee2f75d5953f7211ce2d47ec43084feb860e084f46660b85eb965f008d175d7ca14e3238a8ae0c60984a41ba3ce90331d90270b02fd5722e131e44c51f8e31ef1a9b1bc949ca5b8436af011c971056b963bf33660a6a307ec8bd0d4c3d2139ccb67e32b2c18e0a4ad1934991788b281d12c55b1490b015662ce2663972db4ef87f191567af2f66a267fd372ba7432e96d44e2f4499c101e6939f4b041ecd75849bcc335dac14787109d763d43c3d29739755266617bf2526c68d549f40b85441e51674d3913440b565de4428f583382d733c117285df6f8f2a760bba51034cc3fbf364d7083f7d3bd4e4008ad4176422e8e33a8b26be267497964e7684572cd5d6c6348e79df3a58c0ab409f505539f6c955562fe15b6bfe25063df281672e65437c5b1dfbaa1353ed7b680d5a0715221c2d08f4e64f558f045b041c22b369bb6eac047ec3084f6128954d84d1df4507ff372433bb2b0686cabe7c6b6ce8330722680bd9e956443c9211475af3583f69b06709eea43a3ba90562192ca3be624687e56f03a6ba613afe8764c92139605a6318428b756678a73d9d559fb0292eb992486365997943d02bfd10d0445a7e58034a0253a73d5c84ed6c2244b3b64b0efb5b765f200819c31523585912720f28aa0b63904ea01df9782f542277e03d99c29c2428e1e52c58c29360d7f3c26217a14856b5abb0482b44404bccca6668a0b63c0fcab8a2046125b4569f99e00a62ac895cd4d8b67a74a8a222b9455d7d62c5583548386b6f8ae239459d4acf1d941a335daf5fe946916eb31fbe4c0c1ce979b90844697c7aa584a8182e7e76583ba89c01c5975b4d7709f002c486f87d97323315e561b47e051ff07b1662e00e0e1a6f274d56e537684cc2281d602f2fb6e9310a80df32402f23c44abc970f7d3161551946337968b9f2b416c7076a6c3bf623369a862d4c5a75ae3029d18d4089f6ca5419ff8b319fd3ed3c9d10f33a12eb98671a455867c8e59c58e4c1ed7dc255c85c11ceca5c6bd080150fe10662e7f3e457a1ae436a08fb292edfc9ed1fdbaaee2f65477a0294997b2fd213235366944b69c67c787e90fad1716cf18369821d52092bf7c729225e957e7981f5466ecfee589698350477f36a272470fa1a5dbf1626e1757873cb1d2e47731c742c22c7b4118a2ff664983af61c6f12cc11453bfa1a28ffe640dcf952321294a808f2b3f43bfd236244a622152f1ef75e065fadc70e69e09f28d22fdc13b973816ac4ff23496b28c42442cf53341af35b2c6b34ea019999340545a05c181af31f3a6b0c0d359f53c379c626d3192b46973babf80d18131f8266219b4b7a7cfd75140687da52e17a3f0c5c996e1f54a82f5f988e0d35d8948b05fcbd281870105b472d0aaa3ce20be804af8cb44a7873240376c216732667e527c7edb96477e7073bf9e61b5cd5027b40d24001269ff4b9723d8e9e563c4a31043574dd03d8a0851f3a81194181a6be6ca3457b56f9c95b0212863b4de016587729c2cd0e8525381330add561a1af6921018e6840ce43ce4d9c71c2764643773b59dc29063605811cd5249c580c33537907dd1431b08e4e5ccffd9916152f490c859a437b60d7b67882f8836c4c1a7070000fb47d7c7ce538bdd5eb485baa9e00b939745d114eed26b2e62d4f41d33370f7769e059ffd21258b3fc946184b8f7157ba1c54e263c9517a2f2b65ef4d351e473dc140898e48584423912a57d55f145a9c6a3098293b61dbfe5e18edf62b423a9e6e7cce19bf6b772c893518f9451f3c458c26453c2752a7280a4093391e260fd3ae3afe65bc0439d28a3aecf80373c14cd237786b9619259eac1fb0ab3275060f2b46a442d64d743bb118e862344396349159480b1d31df8add61de0bdc5277102c08905f9833c9d35f5a0daeaf3016678c68972e20498789df3c59565a408b6a65584d24f56ab4d7222486a1ea3ecfe48a08f136110e8e770377aaf66f0c967c8e5fab633876cb25b22daa863a74a207fb41f51806604f1e4511bc41eb5bd16b4d1d53f83c47d32998680637f33c2550cf030d8029623cb39c2fd1fe416aab68bc5e16f8725e08479608789cf10d32ffb05f4d57ab6280b9545881d1f758288ee53a54de7e37a56c985abb4aaf4af8de7455b0693d168ff642572e4edb32a06be45969b1294c40844d161887b86e4dbe6661aabdf06ce5dd0f020015905915a75817c5df2f5eff35e12730cdaf664e72d42849a2536e3ecfcb1dc7ba8a1f61d6aa55fa626c65780650201a2aaf46da8da032f4195e49cdc5fc761df8a475aee7373d6bcc9d373bb22c0f778fd32a5cec721a0465053815b85e0c7d46bd1b606d946b568386531b14345740780c6cb82615197fd195030cb33d4bcfd815235abedf4096bcc844b3ddc533e5895f13469b6828df28de2555d18d4b26424a7595a64f259325c72fc5b3f957e843384918f06b0ccde402101a228558293ddc067b906b559691ab48f315a36ff341ce6a565a90716ee32e44b94716653e5f05229bdf673da330831229a3b17645a22057ee91b5532598ee3e721a4849af337e3fcc01470d1a1d4a7aec6e3c09c036727bdf0e4f6dfb05cd12e09af10d18029b7da089c51d0808e3654c559958321e4e65a83fc52d83ad3c6506c8251ecd08a129d88a6e7a8d92412c673a332940b2340295722337669ad167be96a11e287bbf046679cc719e444d4a5d9e155fa593b840ea089a5c53e8f136e1fe163e16547e159b52c21e9c5be32cbcf93a494659b70f7aec423c5877db598ff74233e9e4601befc0983960ed875193736118dd12d04687bac153fc5edc2b19a8a8582d2b4b3d1771fc7be682832ef7e89d3cc844cf6572212a3c922a6a0c97afcd5cfce2296ec0788d2e8d63432b163cd0389934da683470bb04a256681ea409ef4f8b817a4c2bd4b20496f1071a5c324346721de55baf5d680cbe129451c2cec4712c5ef863b1ce8b2ce99ff1567f85250f2e47f5551f47006af17c4c7703bdc95070cd9466e4855a790d42dd7733d5721cb3725845b18dc536e25a2f4cd0e4084d3bd4ef470c795111ea51b33b8b57ee4ebbebe65f00eb4e1f112a7b2a560c3c15bb37ff17ad07fa412faed96c01de120f976a694c064b890a06953d6e45641b10f297802b62463b26bfb2937c00cd256b5643f07445e34209fe69d54a2e8d644738dfb00bd89929446649da4cdb7b9040046b592db0da555e7da07b35d203a96eaaff1839649bc41abf7320358be3965bf2ec217a59645f00d8131f731b625a06719b8c76bf53342f294cb53d37f770109846ea13233b2f5c1ee2e0091ef0ad075c55cf1b33438754f9b192240fa9726248f2c32c0fb35009ef9bda1c7d3d6f44e1292f21036ace1c522bad3a8622f83593c12d10386ada49a216c51bddb57b7154a1285fc7e6ce18086b260435f1f874a5dee90b3ccf321bd5b8e15f353b335e3e0b2e753d1c25751397c522378e6c45e7f9865c108f6952396cc20ecebc13256a6605537e03f3675a82d0329f895861ebe670748b90a8028f2af4193b99fd53385f9752b52a6c365dc88f76d6416f77e624f0798626f075e6a3d0096cbb5946e22d215df339ca629d415818a252775d1afb6322fe8b693df8605f4851857c70dae66c05a7babd0ea7c837090619a07912db1b2bc478f97102175466e91ba42e05639a5623d480521d204a3ec171fc21b4c3fa405cc9d045e2c7890469364869cae9b27d20e25c3a6e2c2b0db6e1232db8796f2c50be6421cb03ae559265ab5fd62156077cf45266837a9b7d039db12dc95acd1ca2afb828e083e876c2ad4f40e88d570e07df8f03de9b6d788a302d454ccb60382c97f95fe3c9d66c827f51747820195eef89e32c28d89b3463f51e442bf2a84e50797c1771f25d07817f98513124151b4f436b3d8288821488dbc2498a8ac76c4676032afda09c264addba397d515d57c941a217433eef113a2d74428ca2c6705e33ae6cb745402eaf02517258389a772699581fb8323420974902306a3f2106858c58690e08673e5d959a16960d827d3a2a81115987604ac5143d7d6b03120742bb283a5f8ccc0f62989a04cb0cf13e70ac2b2f1c13a84424d71c0493a4095d3884a712ba0b5e442196886a4b9b1e7e4696bd6335567e3aeff0b17e3804aa33059384681eadee06f8c94828972b912c4578d330d2252f5a7c2df91d8152797af7b38c0bd5cd527d89601d0eccc4a36484624442647ee43cfbdb5021e5d37a4d2985652c02d7377ba1e7420c07551e672b3c56147ac9ca384f12de63b405c8557970b12beee9e7677ffd6818f525716a8429a438303cb85a3d87ad512f19881cc026563e36e3a1572236ab5c5212196ff9a7ab1645699565c15baf5ec19aa45091416a0e87c96c56e307520a5ba6436e74a84b5ef4bb01421a0fd939047b713853e6ff7c902a8f5caf42987cff11795feccc146b52080449fb42a5350d53306c197100701fdee776d3de6b15192735773954a73141cfae372ff6be57ed99d603e1c33d4d340b3f1e1005d1320de17f095ce51c3b0b6085374e670b5234f9ee2e3dd74e760f753c145e248251c49afb266a091929893fd20749a57e662268bd77c7e77e2b7951b943fd6671619f19f2768308a87cd1b91a746766de567c3f893a207031641a98320c9eb53a32bc4e6c77cbd969751a4998062e4b9029b11c697166f6a438e7a0c81bba5ae73f50f5233359b74c15a341cb3854cf0f74950bf575cac2fb67d443275aedd84574f2324477d232b072ecb1624367725062c433d52bf247412d80dedd3908e75a4952d8ba2774b16d4922950b28437919624a8e7b0ea8b5c161f7c91831dcd03d324db4a04f0c25fa38c67def61a90d4368bd597c24fa91376e6f8902521be9b21170f1b30591f83654b1007b6500c65624ea30515d1a910a2b096e9c284958d11e5b577d33db5933245a32f46d75c3275685518730f074443a907b522d51886635d3773c086ca8b27cc7f6d566d4c39947de75a91a53eea8510e0d541a7524017113415e24408fbb3ecb61721ba836f8026887d31b9d73af44f18c6b773b813924fd672a50e6900c0c1e82bb0ac0dafc55a201745364dcb00aa5e4dc225ca919631cdefe18b75bb10086b53a18d913883874661714427896586804130f868c1514284e133a5767a54ff45ad750f2fae14e83cfe1297554991e6cc617605672a53a28020a6a6bb988158729df4679fd42634203207cc7ea6268111e182d99432e4b7a7f595ed5b1682e8c037d3cbf605b419f2a193907b3f538529add5df3160b281aeaa07370682f7a0d77e322f0c8ea42f2b9c728fe14611c43b785141da25c2b28755215f7ccc05c4249d766da0eae3a9211e92c56f4ee5ce47d7e5613830a56efb3d544cfee8c1305ea0352c0b3097b55d83a58c2100f3f9e362a482ec26c55c9bd7847e60b350418b0581dde1c387213032b773d1c741dbe990c72c06ee963fa77d33024aa1144924aa03c198da74e46abad741f8bc474813837334e91d8216a72f57077c12e4121776f7b1390bd3cc1f50a06683fe6356fd69129c235b444078e2615ac3cb51d49c21753986c7844b14a8c2619294b1f88f08b299bd1d639220daf410f0db279815d19376e2718711c69c94f07d1774df92adf673bcbed71e4bc4450eaff3b4af5c32956a81ddd449f9d6a682586884e4bafba1cd1816a418a7a864fea693b41f867d16185286c639e34a1378ed2fc7ed687a3677c7bf030952c4f717bfb3f39acfdf95bc837ed479b249038dbefc005dfa863161312976e029fa32c2021b778795a486a6aac8940add37126295a432d257abd180562f20dfbe0466e548e7053e1138262dcb74625c907ff781c3ae667d2551e54c4d67068fe903b3fa242053fd3dc1b004edc7474827e7d155974b94c8b6b386dee8c1d0e90de8771660cdf6981deab23e8010975bd5b0813b6b6e306fb425344c9b70938384bff772eaa964d00c14309d6b1de7e113da57a78dbf912fa5680420290a249a2d92728ee8fe549def1592fc3d4320a3b338f3ea1607e05210846639c758e2a1becdb2b2d51231f41e61a28eaea4320481bf963e0661e01d252e0521acbc56956fb197d2f9a2f44b7d4bc0f2ff62c0fa8b6237036bfc02e8621d66752bb2b694a63e05fc82e482cc1e02a396b6c8015a012b973b251220d971705503ec73638a4289d07fe3abf21402bc00994ecb53260d3321060b2980e51973576d2f0566ed188803a9005746c9c60a3502348be3cc69b763e2758e30c4644fc1fd7b06f4db41235716be8aa1188943662139da00c9e74b54e6bc6e45b9544f44875116043ba661634237ea551b87be70938412464815c2a29f02d5507df468a671ea99e6c83f44d5af5cec9320006bc01bfe5fa55c57fc13f63f2c76f2fc5743bc4a1bd20e490de1e40b4f80c86c5c419f30ed82c315e6f1ae03b541bffda0e664003571d6c7197142f89b85706e07016a6c4f54085a42220acc081165ff4467cc9d9fb00db24a56c690ac64c8c9cf41d219a406eb6b1ae776920ab2970e8fe7a9984de7bc622705d2af9cd116a7bc96d89948e056e0429001fb4a9247117602075c4045f119ef9690901f7259b6309030e357f0defb45719afec260a8218ae6a5c3bdd3724935f0ac6b0ed134e4b9a49450fe572261720297ee092347cc0662c96bef47defd66a7dac37881dda60517b4f892e5e6c99ae70f2cf2f144cd8f638badded6c19d2b30da18ce65b0ed39c69b7f5287c2d68084a1f6601710ff33d2baecc3e120d19d6210a524018f7508e65feceb26d455a2c05c79ced5e6cf682509e13f66014aea4240d792c1194b52977532aaf35cb435d4b7471220737a12a7188f67b50fea8f17e91d4025a42af0e4092dcf425dd60313f3a275a5d6f02786080aca220a48fa53de7d2845f43c98c56d4550c79596b3d7cf1c21f53fcbb0672ffd8eb585d1a9c512dfd7f3b08a49d5ad7168b1dd7112c1ae942976269b9ac4f9fb1ea25fae15a7d3aa5fc6a5aaf374608705f24c88b9e1c2358da23be92b31e084fe05215677933765cdc7cda6cf40ae870780d1fd8dd50f9e9ea1d8fc1ca09eb49124305af2d270655585f40dc800490b9736e2976432e3d38c231f0e3c276725157403ed780192015083b90746e59661409696a261f1addc05b3f53b9a205a0334c51dd52b84f407b8a489ed39e28fc267b358a23ab57db938800113ab76249052a60231e69547c90af5fb8c1797106f38e4f6087c475239f4a0cff5acc10a0f62843d111140a26c1b443dbc1937aad1f8b21a40ef7149b5e8341282aed43e5f9e1355eddaa637258c27953727b5d29ecd52e70b0e40e0a706d5b2ec63f0457e97162a6c1604379813b6d84d7db03f8827b025efcc557864d04739fc2bd28794d3e2f8761d4793539de06fa479154be36d120387e2945c68aa4106f0cfb3a7fc30265957b2d70cb3dd102b72da842a05bf17d51b7d735eec14d5487848a6f754e653b9bf94d5624109645c27a451864981f74e3592600413305338964586052e12f4987da983718a4dc04f8799c261da8a51e329fe253c61aca0b4836e83c2805d227a64d30639105da205b1bbd602ce6966f15a2f57d75b63c05fbd5f95bb6398a12df70f0313b63702a9da3622e48e0514dc9c46f1ca0cda65de957e23c85eda26683fd825d0a7b5c683498e45135cd1419ca282856f1a1124bb95a601fd063be0d6930fb014bc982792607372adb87201f84ce293165bd853f9dc9622a8d75070dc90d8a297a93eb385b218e2fb081964021e37a7618b388444b6cbe0b1f009f18c6d0b47d71165331d31ffe24d4d10626bd24d473ca591366c2a25f778a80537a153af425ca34c4485d3b302ca778de140a2c51305b52321ba8680847c6b99f4f0edd1c6266dc34716c771013b628f7422fcbab15c3725843ea7c48770aee5747cc65660436869e3374f2f34a939bfd07531b69191acaa4550fcd4011bab5a2525f1d531a56411e040c0ecf5c2070092b55989d2239a950129df4da59bf96f23ac3407d7ec73cd20e47988c78ab3c6c7d58730b28219d82569dce220d57067d5adef054182d8e8e123c9b1514b531f26da4c174342491dc19f054d028399929634fc01406e4cf9a6f652dfb51ae7a6464c118614a33456e5a9bc364078d61132f6911d82a084e026e2254fc0b0077b73a27335d6e5f571b55555ee53fc1fd90514d97e25b8138b105e68371442d9af2576e47d057229b427e42504b3540143432bb9c6b7eb2e87724d37aba450f99746833644467ac55b724f2e54732c792fc4f28268f04cf2c516556f4a51f392994627786bd15671e512ca7c9fb1d1b42e030c255200de2a2c90e2cdc931776a70562fbcd224b7a805c3ebb1c9d21a73bca535dcbbe404398ea7c86973270c91cfc2e825e1463a18583711337db3c142f304b83cffa5e6637c13f1f9ebe24c9aed30aa36ac31be7a0194255bb0f6252450f3efa6996007f1fd650ee71da2654a1b53e1c65e72a022d2f5703d5f91d52f3ed160a96242263ca19784f24492876a52d2773b2571107d4a57c92f7f17461014c0f40c57264135f954e14aee14d53815333f2f97d0bdd385d78f09eb14e5516384507431a1867c81a7075881b40ba7113676372fd7dd31a230720e2d41e63fb4e6950d6282f5a46485a7cbf6f4428001f06fb505021bb611f148d8cff5f08437376dd82a55a7c134973725a945e39ae750f1b340a75fdace4689e13c11488369d0c33a53c397ef8d52dde26cc19f57d845e943f6e12a64ffa5ddea6785474bcf33935123d5d095cae5f7e35932a17866e2608886c5e299b2c542e296849d0f1db42f77a5c13638a380376512a568069081512236a5a9bfe0b25648ac44d78f8066c8e393e09ae17af3122a8a433452bf73ac0c69a43541f8a104323a310ff31815c835e702f19991c502060ba26efbc1060d6e59c488ab31f06ba13587cdcac245c58020e19013bf16be3a60072cbe0781c5739f9649234843e96f321124c26eb795bbe7248e192e56f89af3401d3961e43d2f4c4047047743a677cde3f8d62d20dcbc4786d8cbb84192bcba344c81d5002e0d135255a73d41a21a72a3c4286ee1f4890553981de4b1f6d86161c40bc2b7acc63163e1af5ba31b38c92450243f052fce15262b9e1ad3074e05f4e79a1c97211f1d14cd3ecc8297a3a6115f5739979b58e811b27d3482749db6e7b600832651623e83b67a4ee6317440f2100c4df3c3e1e2f3e4bda7c2f7ec638018b253939c225b525f0657836bf7af071a50b0b7cf5010e47c6272c0e4604964910d0a34c9c5c7774cd19b82da9a13d22b0f4cc5a63fe1e41d4e38d6f7aa25d12f113e74e7cfcd028733e4f1fbbce14488538ca1fc2151a05c23b4356d4e6695cd6b8b4324c094505874119227b6cd91fe035150c0452aa0760b0f4233a9d785b9eb1e43a7ac1144b9ee681533aee563eac7cab720cc34672bad53535795bef59fdbe82607b1a0d2c0fb70f4c4fbee44cd1d4cb4b1dcb65176dcff42b7b7fbc49c17d1f698ecec7749744057e4a3597390e6c3f4f666a86104f6b1a2ff2da8606ce827144ae36d35465c5dc26a424701fc3624060a676221dc61f1279ef978b47235c1d0eb259c8758d495445c483e4543dee5947e3e8ca6b02893759870e094195798627f23fda6e4ccfa92df46a2c0b847ef13bef974d7ea4adf53ac3827c3843c9c3383e561e0b33e724620dccca55658d8b54e291695c2e2abd6c7358f37d6b7e52607c40515ea9d2aa3a61048d59b31df94a140c7803b871a75ff9529b2e5e5bd32de93d4048b61c4d56c79f78041de38a79f4af57417b2eff0a4a0db843890144319ff71e3fc9b84d6856b3fc1361a9471b915b2710b4b83b353029fe3ea195971ed824f92cd553a025c4098f3af37cfa7af738580802f702770d22dd63548a6770becdef0abb9ee02aa4da1806dc3245594f98f569f0ac63630f18671652882960c5fd82711984611ce0a9c4525ef7ee55158bb2553b535676c9808b7e4d1bb2223c4154034d1dae24fa00d63023d182331fe9515047a79b70694a4e1587bcfa2623699c046cf7b103b6d77266aa04eb4fc07ea255e3ab966ea7e9985e2e51120ea39f422eb979fc2789ae1c3eafbee154215d3b558d9ed774173d6e09b5057f3f44e06d49244b03518da35d4e9d0512327a798d1b927ea006fd0ba6555f42ee69a87ad00cb0afd87c2dea404a28eced02c25ec27a79e72808607f5022d154e970f3be05635f98e10b5859b62770aef0040f8d3953e3fa5361be78a31a50c98362ec14c966452ab82789d35c421223773408f85d27622b373844caf639c2972963b9076a10af82b75759e15e00f2a8da678a5b88376075833dac88b21e81621a73475ba536719198626bbf6b12b9a0465b83c9d5311e70fd73c35aa54a78801647d8774c34dbe9536db6734337e91e9e4ea0dc957735b43c4561ab1612c78a997288b90d3e3b3f400d17978450a7f20307c3edef006210874d2213f35e0e114f5f320481760b19ae7e8872f03ae7cd674867a9e11ec9d480551db4c554e1a2fa08335925757b5d161f95086f0cd628f714fc4f4c57f2930a3939f2716bf98a9d6585d9244fc436fa1da52ae01d9d77015175bc641d7116dc52c337a13d8ddc6f3f66174a0c335fcb1344ae0b1d293efa3c2b0ffe3c363d0024f3142a741a2fef23dac92e1a5590e55430cd5d6cb835bd3ca96faf7ef2ec1c06767c4e083deae30b5e96c0171336203f13c4d10058c1c95c4391306df980680f18d47079b7dbc01ecae07f2ba2bf9720f4c40a656da7c374f754894abb257c6f2908606019a2fc65bb251c61da85f454bf5f8e231db7307ebdbea03f64a90022612ee2575718de531e9c877c5656fa01e57e8f2e5af5c77990fd9b39910248199d883908de7d2f1b55b92d264dd893736dfc76223c4a6657fc99302371799524fc9f6d1834c887767480596ff276ab5d74880725c9c387768dc2a33056928f0e50cf88213fdffc75dbd35b53db8b6341c45e0d4771799321cfb96c3d9d72ab2ff8bbbb669d672a386c89a54081cf7e26015791240498752ce8875e40c6141f264f17a653028f962091eec14839d9d77e798f5313aa2c2121ae869f725ecbcc47b2482f23d9446a3e64f6933fc0313b2a65f53d640395285be771d224053edc4dd796077c0e79ec7d62c9f30848fca230e9a810743bee060ce7e4e13ccfd3ae484cdde23faf778317ad88843a738fc634c791fb2cf6dcb367798b0a5fd2cfd5604403da5457ce096d3af4593ef70bac5908b7663d851bef7ee06b7042a7b1b630965062332e59762bac095770c2a3367b25d0715438231e58e1a0ba258db5a30534e764077043cc3ccf8f7b6b325d287c3033ac17ab50c36416f0587b37970e6f3af35564827c2d779ecdd108318bb062a15db159f2f34e534288595527f4641a3d12465774f31a6bb8a1045f4835a9595fe5b4121bfd18731685e37a162a4f22a663bf4f0f59e51377c5b2123638fb3c3364744756025e01075bda36d4108f23bdfc3c52fca0647489fe607e870cec4d29aca3216d83474f9625cc727464c10018fcb5079d4731339ee1515c2ac4cf57448eff28037a72216abf05738b08f42b267e8b360e7072312a01901223e2eb14320bb96b6910744925449f7e4f354330672c512a75ecae2af427561210f64972450b45297c0347621e12632a3be0956789489458ebe540773dd56c4fcea5f41bf9be8875e6bfb56378f8cb47a884d24926a7ff4930c6084d9bb94d67a23a5e4f192ef225df60367ce4631850e0cbb72e8ca15d0789f42d17e1d0ec5860d3775b305886486fad9348736bdb439e0dd61566bd26283f36363d88ba3064f4d13551a121375647f4aa62700e712d5bcc9f09691b5e56b7e83c28b0e3bf3a2ef2b022b161d269a5511a108ba54047d7343109552c906cbd98760319a5e5074a0b6928a967641f5509627e1aca7733f751df135a4453086160f539fb2264678bac78542ee9191536c0575f02353d7e8d5d2d08e5aebf0ff92c2f19ec490957a235c04ea4e4380b22ca4c5a38b8064abd523e0277c64c66e93dbb6527b5072d2ba77a2f84b016448cfeb409074fcb2e7000fd09aabc0e4e2ca6f374ee328d36b399f9749539f6433e5ac268622a9c418e9f684c43df0b162c90ec6faa3cb81ef5ab856abc2ce803ab4ba039b0213802bc097a4bbe05043d578b026ccda40d4cde5cc2741f59730758280f057e786c3b940e705bd4b7d65be1075155938bd7244d5f7220c177467910718d01bdf1d96e30728b6e54e3571b9545bd19ce23e55f4270dc78d12b352750207f4c660d4b5600bc372eb26fdd326f762a7280585939585a484b0ff2f34f081cd6207ed5c91b8918c814ed94570884247f3cd2e91677b0564376c5f5e5748431745aaf97e838697d5039dfaec513eb8db05a84f4c80fb4332913c848a04b11d21d2ff5942f2385419749f6d273405d91bd2863c0b97e7f28f85f7b894a1b1356ca715e8f29050a33fd2556ee6c2f4ce4f6166fedc60129e9467572fbaa1e9fda5d39e40c31110c3ac5532668034fadff6157c95d491c03ea6f03e662a20b8792741d2d887c2483759e17c54cc4569814e802271ee25ae5d84069e295ac7e3657452653dade1f24451109b3dd3a56deff596fc9af8826bd1845331edbca7daac2cd4a42840a06c8c013565cc5ee1a6484ea4e150391707221db1342682c3ee27f712a223470744ec4765a43e1027e346215430301666467df17130637a27b989a5154b0f73867a2b4917441a7745de24dd441dbc8137067137b7bc1ec4917411b5b257b4f77019f430f59c9b823646501bd4d94ed3e0a1380ba053c95440c90429a0b3567e62b13c6b86a2b27515006ff8c2b7c66a52b777eb360e525ec7411cedf1f58f77e3d8467f03a699db0311636ca532c82961a703d545da8e5733d752aa36c519fee0a54d2820ff9ee94067fd329181ec1b06956855359a4ae604a8424891c4b44a247612f0b4ac7677d15a0fd7739c9ebe07334790305eb8915601cc07e6c71688c4cc369fb0b264f4f0b5b4e046fed1aed04433ed7535d3c6347e045e6734a4a6a31cebd2a75c8a7f770ac1fc17cb399f75c6028ff062f500838fedd427e25f0b77a9252926554856f5eb132000e81994944b59323497cdcb55323b3bd7a67668c5df77f4963bff516345066e0639be1ad2e1edfd61fbf168548d20e717c388e31431f63156e3a2e2e5f684c0a42f6474441c9c94a5beda758703aa208053145932e5e21061dbd13236b89889a7d9cc188320659376ad167747ed8e239009c7756343e72d13e0bc56a6847fe73554b0de87c31d8d70e96f9165e7f9c7a583630d87b11fd6028fa29f27212ed4d18792a2b392929235ab2e2944715249b7df0be4d5e3cb4e540a0a62249f202950715249b7df0be4d5e3cb4e540a0a62249350f68499fcab759776e6e09e5db277b0d9148490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466ca4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466ca4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466c3dfedc453d16e14725e4cf64569a502e0fa8421521b2556a086ff867aef8b019b411d02ae76b9d30ed745221dd9f5b46d9f1ca4dee6efa520802db5e4b9ba13e78a6c270a77385647e18eb31d57737632205fa42b6bf6703769e1407f4683e39e35b2f15f6d4d62ef69d631446f35f1fa3f3583feb45630555a9b700137a7533caa4e757abbc4a64d1bab05dbcec1159fd0c337d45f2ee1af52a976527016d3e65275f6de5568c12b01aee07eef89522998f3635aad95c50f0606a73d9cb7a0025a6f42864748b053c7f3043b3c2fd4b91035c4b1d62c82db04d9469a3bbf361f817a2038838f80f6e17a7776e7dba622734932bb5dac759c45f83230bbf5c6a926a827b5a7d8515e640816a22ee7b296922f760b55041663b4a494c3245b16f9f19772768cbcd1cd3377f39e6baff3999a23b0fa0e6e046fdc6025adb214364c7632f4570807a144121a8769ae8fe1711e6532e8e50ce4441f9122d507c283c0013053b28b80945c838b55b07d6b0268ff3420daee7f13ebe27d40db232ff337b1f1c57aa3f03456a16250fc2a35d4ae93b6260675c0b2c23987576cf975660671715603ec19a54a5907c7e16e0cf6a0b5a3c1fe453aa381b72b9701154ef46966c510748307f1eb9e9534e3a6647363d513b112181a84b3bd99b0966788f1c33c65d0b4b3cb904f42c911ff4ffff4bda71a53329ef4c0818008c5e5b417f1ee5fa717bdef15159a532b16d000000005819bf5d0875f9098f397672a51c773e8ba8f523e9755d2b4d51a25500000000a9a6bd5e75f3585bba7d015417c7ac746f90de7a000000000000000000000000b5252a0a5dcb911d18de6f3a1c2d531de754da7d00000000000000000000000096215622a5f134011d8ceb5062e0700f547a716d000000000000000000000000b94e21612bcce355043d183526174a055bb3a610000000000000000000000000e31cb80aea30587b508ed85bd3177f39452e4a570000000000000000000000006347801c32991335df25192a1c8b125fb43d4f41000000000000000000000000539fc537ff6c887dd5ec6b58e1d4c77e3cb97227000000000000000000000000e022631ee43c3e18f622614b48887d194c86855c000000000000000000000000a005f653b2ff693ebdbe49022b09473427b7d559ae8173406b5b690bac1630403b9a3b068bcb2404247f67065fbd765bfd1075407698f35c5683f73b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa214e5c8159f75f02de7d0174f9bd350431367d33a6900f33b3796700000000220cfe36a4cbed5690c46b7214a2307d18c96048222f7419d148872200000000c9c7b439d54854699756167c4934e3208c6c15390000000000000000000000008aecdf27d32729696f6625032bd5b310dd097d04000000000000000000000000af65bd01be9a717e9dd66a7c3e462840bc0b6c1400000000000000000000000026448a789ddadf441157822dd75d835ab2c8de48000000000000000000000000fe3c3a36ff232f5f891ff417ac51995a503c4339000000000000000000000000cfe0e426deda0f6d94d48b6720189e3babdeb11a000000000000000000000000ec54c04459c46863d7db7b455624d64057ea5a6e0000000000000000000000006607d8106403782d7eac757d77d7235c038b5504000000000000000000000000023be52771763825088c202e382c3622528f6e38282c5150402d8f4bd5bd4f3b14175256ff90d60c0110ae26921e0103a7cd1f797bb28543fe2c777d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7daff24e0b28f430b483e12c0c3d551e94b672b9a573263f46eb35300000000cf093819e0e8a8063c6d675e0e61542a1448e24744763e53d757a31b0000000095a1e66a38a2ca3be2bcea26b030034a8ebf68390000000000000000000000004c2bb6666edf731165cc2c5d4577e43816a27677000000000000000000000000e45fb6499544883245bb2e0d124edd5334394f4600000000000000000000000049d3866245f5b31807767c3f9664a06ace6ae65600000000000000000000000077756208a926703bc3bfe93ccb442f31248201600000000000000000000000003dc1c2033fcfad66d19a06052980f34b55ed796c0000000000000000000000008b401e24aac0fa122a7b7f45b1f73b09ee2c2641000000000000000000000000df817a08573cd66defcf5900071fa40902336b5100000000000000000000000059e12b69d85af0209f675c507e538e12a952a857667e3f39c695dd7ca54fc566fce5cc1eaa7ca955f1ded20fb3264e4d195e8e5dbade6d094304752e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083419c321a47e8753673923ea50666301a61af607310850c4bca230b0000000076b2643babe3083ee0092f08de35f856f509d03adc1cdf5a45c8323900000000c3a910323624e416180be54f37cf284f742d936c000000000000000000000000aeb9c85deb934a28f03a0d67d5db692066aa600c0000000000000000000000009946ff033e2500220c1ebd25528c8532022f3d05000000000000000000000000e975227e229ee306d1d2b34d4f173c0a152369300000000000000000000000003b56ca449d78ac0d626260652f8cd62f82b4d7350000000000000000000000007aa78e0cd7f6e46aa063c974b931f3418d079f430000000000000000000000007823f74a3f594b13d0f1b96073951540f8604539000000000000000000000000a3a6272f3210e333544c836edf06f76ed40fb6300000000000000000000000005c3c642b9600c1706ed6033bda20ff622f295b67be9c7a61fbfab94100e1dc5cb2bbfe365bc85f33c511717d741a817023d0aa3df7ef803ed3798f090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006789c35a532be5459f20a82b40f3e72ddb1ee229ab4aa570ff72c41d00000000c8bb252123f42d720d474a4ae8665a6bc6fee01f3de72f47e7d82c7d000000006b8c9e47e9ef4802d4993212cf7a197b02443367000000000000000000000000d762f13daced2e6345147c7823f3db1a18fd3a0000000000000000000000000058159d582ce4930d26b6c03bd78ced509e38f5710000000000000000000000007c91f0756162e4141497570b8fbce42e33ecd10900000000000000000000000036386f0e45a2e705c76eee754f0d1414b3d1fc3e000000000000000000000000d350334c0cd6a84a8a1e3810b36fb2007b67903f0000000000000000000000007bbce82ca471215fd16a167aa37597344a744863000000000000000000000000dcdc6965c60ba323a2a7cb0aa9b6ba5e8aabfc64000000000000000000000000c3431a06a6243c51c6a979638512951ef70a4165092b8d6531369c3b55219c18f1276d333a1e7773889f6719521ea464eb81ca75d186d84654ab830f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033f1d78c63ea771729fb7434a1d701bdc1e9228d8fa572bc57dc257000000003bbd394d7d56b52214667855dffdd26c576c0855c0ae5e0ef0d8581900000000dbd95229b2a59b2450d2a87c31c93d7c0f0ce045000000000000000000000000392d44226683a82205a9d1095c40c619b81f8d62000000000000000000000000ec38b7300500613944b70d3c965c6613f114ee34000000000000000000000000337db47ceba0120fb0ec477366a1514d4909af3f00000000000000000000000052a53e1566b2af0ae89047572a8fa0548a29585c00000000000000000000000007ec7f407915632dd1a66537c4673410a511b7420000000000000000000000002d0ad52dcabf45272c51453243866871c1f9e619000000000000000000000000f079eb42e17cde642bc2c519e7b1db3f546bc4010000000000000000000000000a70b22ca63d47632f5c517c233c0b228addc610879c065b95b0b97e9a81ac3be87e0c695cb9144e55fbd756f70e40748f436d02dbebb76fa8e7602d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405d8f3bc2ea146629b14d154a4978734c5459269627894bd6c7260400000000b847cd17c14e9f1af6899562c114953f6bc3185e0ee6ba6b225e647a000000002219621ac1b2e00d575408077d5549506d539501000000000000000000000000ddf2494641a5e5314e03625d57f0a5127888e8610000000000000000000000008b0e264cf4439e17bcb9810480901b516a15516c00000000000000000000000006399313c956f85a3d5dd31d37429b744453ce6f0000000000000000000000006be0e6627204581209807362f10ca652ff04ac3500000000000000000000000036890950c1596e743ed5935f50958d3a6a46110b000000000000000000000000aa47301ca5b2ea5b1d11b1507714a50c15cbe615000000000000000000000000b51a486ac4110761c86cb96cca0c0d310a03f35c000000000000000000000000327fb076c206c128590d9071639cf10b80ef8f2d4f131c267a66941058611e6cd3610d0ba1e04a74cf644872e5b10f3037cf244cedea850667a2f67d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c742132007dc8776b7c17a4fbfd9542dffdd9e3096b77b52442eb44c00000000db85d636ee4094335ec16541fdceaa587fa22a2d8ce0782ae5a3733100000000be85dd4954793e593e7ad32ff33dce01b73ed4010000000000000000000000001b4bd56f839ef36a7bddef74bef6f47450bae65b0000000000000000000000008739965b12ab1c534248974ca0beb8057fad54030000000000000000000000002290615a5e11595d29372966811a9c1d2a2b3c210000000000000000000000000b2e381e88054010f0e0333983ebd12eee1e7e63000000000000000000000000c3d7975ca2e160581e4f8a4a8dc1b74058ca6e3e000000000000000000000000625c2456f71f5c02ca577b43f8ba2050fa4b343d0000000000000000000000007456d145c7add51cc7aec2140bc8b9166cbd08030000000000000000000000004fdc112f588783726e5f5a4a1a59977292881307f12f004d86042c595b695916325f4625b996bf169551b72304d59b2445dc3922f6c0096e8ffd1d39000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa4c13321ce5e374f3726a27eb9c5e7476343d768584492759585e1100000000e3de212c2c725b328d1c001aff7f65721cacc62ef0cb2532aa2b5c6e00000000ac403a53cf9aac11e8dcba1924d93e551faecf51000000000000000000000000b162312f8635de491a19b542b4bd707dfb3d152900000000000000000000000083faf860ce321901b6ea4237cde3d06e9f559746000000000000000000000000d9b3f063795d970d2342837d205e8733625550730000000000000000000000006a497f3701d1296a584e4935eebe8d268205d55a000000000000000000000000b04c9b6f34298b32b808f2144420750d1908464a000000000000000000000000ca3b0d37f03a2a1b8c551e7a8edbe55522b394630000000000000000000000006079f00888dac336c0009369964eee37fc7f7c7b0000000000000000000000009542f94cbda0782646006e5ba34a366565ebb16af22b1a157186b67d16fd803f05efa54ef4d36b67c3a51435051e1c2a3edf1524b7ab8e2a244e5b07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba6419623ff6582b7153d85f2c113f2717526b505c753c6a357da665000000001c9f861ec9d6ee2296ccee0c2113fb29c8dd9a3e523c0b5df54a4e70000000002979152155f954121ca3b73071e6dc7356add74e000000000000000000000000ee22da2e3f155c4dfa297958ea9a07320b3c617c0000000000000000000000007618b048a6779c407e19b17b6f3b941c5e0645450000000000000000000000003253ec6e8544ba7954bd1f50f5ad1e5bbf79bb7d0000000000000000000000000cb6a25e10f18252e7e2bb4919486a418fb3b4510000000000000000000000006a3e8d65edfa3e64fc82d86fc466487283a1f07e00000000000000000000000017a7d1658791952a2b770965fa1dbe3cad66e739000000000000000000000000ccf161093ee5217454374a285b66860bb7ff4a240000000000000000000000006beca623d93ff61879a86320ca39947bbd31d0099943a61abf2935439b073451fb08c85e8bd1223ec80ef36d200caf01f1348906372ea22f298966300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a9a415391bf613d9621c1227454862a6a69a9172b1b5c4de0337e75000000002ef43c479e0d3c08b7b9c351ab24a2582fd3ca57049d27403ebafb05000000001885b246d679820b93acd20210477a31d56f110b00000000000000000000000054994d503de96422fe653519ebdc97368a83120d0000000000000000000000001817a24f50ec800069ea76317e7ffd282fc46336000000000000000000000000b143c96da2ced04a6b75740bc9ee3f1047e8a20600000000000000000000000072d292318cae6b6de4dd554702859732c964e1710000000000000000000000005a132c361856c439661100791294856c11857e67000000000000000000000000dab2ab369e77183a13d8df1c5467fd17217f7144000000000000000000000000031c3d6ef670f11d2f39500de7d0466fd72948510000000000000000000000009b7580259c902735e7d4e70f22d78f01c768306b4d0f451f2f4d471fb6b0a41dcc857d7d2a5a6f5902eb9b72cc1625497545521054664c464350156a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b07b3e5aea585f127fd5aa2ac100aa08bd143b39abe4f94ec9f0143f000000009823507e6c5f137019110c7a271bf434277deb52881c095f023c614d00000000bd1c507a769eeb0f6f43622a4b79113202da2c17000000000000000000000000731ab534522cfd0a22242045d836295d4f30730000000000000000000000000006e3c778ad20803f3c1357107aa36429e62dc15f00000000000000000000000083438323fe7dc43c52240c42dab0924830e482610000000000000000000000004873f01ed8ca2b69afa64a289341eb76fcbd2a7600000000000000000000000085817729002a352c77c979592b511713a938c849000000000000000000000000dc0b97266252f74a23358c260122840dab80433d000000000000000000000000688a92139081ef361fdb6372f9746d51f5472d07000000000000000000000000d62b5a1545ee0413d6ef6306804773322420811dd855514b58cd0256701a4e074896c113bf29582406264362dcdfa26f1bcd501bc6790860bb63611d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a764f798688da6fd54b950457ef5e760f439072b1e35a63e8bfcc08000000008ce21f645d8b5d760451d94f1977c22f2c73cd70b82de46b5957a6490000000072cb1b512a69440ad47f26276735f9565d3e141b00000000000000000000000064a3a867eeee157a3583fb041b1e36311ee26d2b0000000000000000000000009b0c611b81031f7cb4b52c3e451f300ed5691551000000000000000000000000bf1a1560c8546b08f17927619d9d4a0bfb75e10a0000000000000000000000001e33db3ef42c3439e028ae2c4688b3708731be010000000000000000000000004ce0dd477274ea72cb7a57644fcc326efa35e96f000000000000000000000000c8f3717964eb0336e2f26c4048548c65d168017d00000000000000000000000061b47640dafbf31f19ec75366a4a994c4109f115000000000000000000000000aa3630391f014624dc697e2e8f5d6d53fa64770d17eab7054cf3e627e0344c512b97431de995d4769073805ffe072e2a9d20587e24a3d60f405dd3720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4e60398d02bb31ca553d160adb4155828f315876188511fcefc05c0000000039abdb5ffa559b56d27aa476612e822c221d671fe5074745595b241700000000bd1e0a46a406531716e0fb676cb5f0407e925a09000000000000000000000000691fc801ef2b965fb9680357a003a24b6b88cf49000000000000000000000000f50d4c264a4963622bb0646db5c4673ffcb29119000000000000000000000000dee36e637c9135448d31b36253c0b0309a3fd641000000000000000000000000bf314d3db21e4f412cf440222042654ae08dd30f000000000000000000000000fc0ba10b63c74c44f1df621545a82b2b4339cf3b00000000000000000000000028dcb6099e57fa74e9272454eaf9b173fcf862510000000000000000000000006682533b2ac1e03f4fba1a157752ce4fff9db740000000000000000000000000220d59594cddf612f67ff66ff93eda3c939dc07800cc3f612b31f474279afa1456a4776e446c2b57d503766991d26751a7bbbb56b52b2b030e3b987100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032d85473c0c8ad27bef7187bdd72741537dde823ef72a42dd189c930000000004523e17cb1bd453313a22e0aadb062382a850449e26f781e18b94b7e00000000f1d6336a39e5255c72cbb56612d2b71cef290c5c00000000000000000000000026084c216b137d2193842862879c6015ba94df62000000000000000000000000c9f0a876bce50832e3a2ab26a4694b03439c97400000000000000000000000007ae77a06b1078872c8e7b3767980474eb13dfd0a0000000000000000000000001c0f674119ddc30fb17b307cae80e73aa54c423e00000000000000000000000011d2f47690dbc931a184207071d49b49b62d021100000000000000000000000053937c04409d0701c8a8ae16fcc7fe1665ced67a000000000000000000000000c324206e4973a00f95f4f33dead8762eaf75d824000000000000000000000000e5bdde35e43a0016d1bfcd59ab89ee4b551fa779f2895e546b4cf67c864fdc5c8e078b6ad174e23097d51161eb0da91897399c1b1c55fd450459df3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8073938a6d4c630ea02974cee38b32a5c2eda3470119e6478c93b3500000000770d944c2b7428708d4dcb5d8a04976188ba9d5fa4106a0ffdc2f022000000007caf937b60c8c9620a43ab444847a079642740530000000000000000000000003262897c1627bd7807735e4560196f34e9f094740000000000000000000000004f34c319def40829ca70442b9ec3422b2219013e000000000000000000000000ee25d93be9e47d657699a40e7a319b6c8ab46f0d0000000000000000000000005cbb8f67617e0d2810a67e3553bf0617d85fad170000000000000000000000007055c04526bc12797cc0f01dc08d7d0dbdeb694900000000000000000000000081166956ca10cb6d83991d0569d96f0a06799827000000000000000000000000ddac8d46580d7b418a8d8403525f3a59d9935213000000000000000000000000ad0de767eeb4653616c4692a92fad27e5ab287553d645d3ea9c4171a214e92219c597f2b6470a733cceb0569606f4069924dac2f8500d338cae6f4020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008411e3465ed487132a09617ef1efba2f27638f5093b43d0746bd4178000000005e9417395f592a52bbee034520631f2b97f43462aff1480aed860c780000000074684673df79da043c2f003b14938d0a59fc87280000000000000000000000001ac3395a78e56b6fe6dffb61519b8b06b5406d1900000000000000000000000083d39427033b3659c10e4101cde2692577615c2d0000000000000000000000008004aa0d0f858d38726f197d0df4ac1b7634af5b0000000000000000000000003e4c801278c71e77ec0e2c5b296baa0c46752f2a000000000000000000000000aa7e9b08741a160ed3caab2e5060512877ab550f0000000000000000000000001b3fb961c946b709218f4045bd0f1c48aff801680000000000000000000000003cbc0a23cd88000ad9fefe36c148965106de8b2300000000000000000000000061bc5711a84b5e273f9a1576bc342e3f4f7a5b24055ef46923628f04114cef6d5532762a8b7b5f2af06a6817890dd04a361bf0519b164c269221d72800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002740870f667c23588c59a172807365c0a7eb22264e40c14ddf0202b000000004a4f1277a70d8d718486806fd82efb69d45719191a585c0cecc7f173000000007d7a10661a261220d74e322109f76162751b6327000000000000000000000000441e633ec0fdca6e177b2f3a0c75d06f54d6603300000000000000000000000047a1764d61ee1e35a410aa2b22480670c606f345000000000000000000000000a8c7d947e4e49b25be045822fe35b6785b2816690000000000000000000000009cb47c74b6c32350de0e38000c47720361916746000000000000000000000000f768aa020f52ca0639082f724d334e52304f9f28000000000000000000000000dcb29d73c7724127e4bcfc625275ac597d945b1b000000000000000000000000da47893865233f0ef87ccf54e775592cd5bd6d11000000000000000000000000b2297054cd16780f3e5a33200c2dad3b3657003576274e41a5191c172ea5643f27145e716252c400cf6f156f8a8bc460f9c4f1618c45d740bc0b8977000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa4a387617a7fb7e9d94b92ad33c971de88f8e13c0ddd3156ccfc04900000000ec0ca34d2061eb4064a7501e206c425aabce7a3ee6a764324688062100000000fc9cfb3b0de7bd29a756691516d7663f4b5bbd4f000000000000000000000000a1d9d65b7a7c602016f8ec75c2fb317702654e73000000000000000000000000bea75171f3a0bc28a5e5394034e9a91a2952b80c00000000000000000000000001934d08c2cd100a312a7243862d164cb4db362300000000000000000000000090cb805b33d2302ea0bda86edd39b346cc701054000000000000000000000000f887923e1aa65d5e7dd7f533a013102167ea7c46000000000000000000000000e7f37424b671db106330ca5d2b2ef17a38c51b360000000000000000000000008e351d3aff970e14a47cdc6edf755b58ddfb363b0000000000000000000000000238064afd41b512f65c96149f83c0316919d6726678df0404543040aff01c57a7c011594177362c7992942500f55f317083321df4f14560ebd3e05e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e3b25c794bcc9721e18c4854d9cbed2897f04c7dda25d52c13a8cc0e000000005263f56be50cd65614be235112b76b5e04886512a9acd0123ee1db730000000023ec6f168589dc129477916503298103f50915040000000000000000000000005e63ee5713f67671c4cce859d1f61228926866710000000000000000000000007c4457069d2fae101b0ad96257f6f10f087dff68000000000000000000000000ad23c20b01a0e3759e616807765f427aab624959000000000000000000000000bdfc9d23ecf87d506278986b3b45f5607588b7060000000000000000000000002280c11345ca6e266102995018e7a25ef984c568000000000000000000000000ebdd826e66d9816305f02148369bd76baa20f82e0000000000000000000000002e41c767b5c48a2c4bde3f12fa893c01440ac95c00000000000000000000000044641c20f5e543087af2b07e5be7af08f4a6bc77983d6663c5423922b0ce416cc199e27a17bd8f70c346c4218959c47dbb83b35dcf34a2523c0945510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d57665bb45ea6152a9ab718fefdc01a7b56665c0b65004b66b3af4b000000002bbb006e75122779aa732e7e52a99d3b1ce4ce702008892dd8f09e4c00000000e16b156ffdc6083c0e9e6f4ac58b54787e5dc16c00000000000000000000000050084e2d8942997626416f70dc2664089dd5e370000000000000000000000000917a40788410881f327cf526a632d133430a4a51000000000000000000000000acf2be68c9447448662c34396bc9e70ca5894f74000000000000000000000000c729fd65cebddf73c183d1757088bd56b110f450000000000000000000000000b69aae295392294893af0a5c3daaf26933dd6b54000000000000000000000000a759463fc98a941d0103df1139654c2b24449e7c000000000000000000000000f6b92633f2c0bb4107890f614b24f003f9fb402a000000000000000000000000c19bea3feabe1b3e10e49a3cc7a2ea636737ef1ddf5bd851745c926689c7ef66ef9422704cb2cd3f56a6fe633fedd0718e465d569af8a9374e79cc410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a75fa4115daac526ea1a5110ee63c427209066c35ea0812764edd1300000000c7a3b86ab9f2401565521b30a777567ee2408b106b14204ecda1c15300000000d1f3da5badb75828905ee0672e4c2e4c8595045200000000000000000000000055519d0fb505977e1e033a7d291b7f5cc806030d00000000000000000000000071941227434872265f9ced239e3d677b468d0d110000000000000000000000004bd3353525c18a42b7703c24ad0e8a0a6344dc07000000000000000000000000c009943c68c52570cd430b3c242de03a9b5b44550000000000000000000000000021f0120f2cd45b77e08d139ee9935a00d52e77000000000000000000000000b12a001a8274b075a384a752197c60439353c11b00000000000000000000000067ba1019ce2d9d1342832a114dde9f258611897b00000000000000000000000020b5240b197ff95b38c8944a176de50180de5f261f5390536b733d11d8c0a42162cd8449b2bb1c04f4357e14e230fa058ca20b25d64da47eca12ac14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f237823fe9aa592d40615206a8d137419b1ed926ebf44246fdeb7b5800000000a65e5a15165b63451f0c8e5e95674d1359d7ef225b8da077c8fac71100000000fe3800541be51e2ee66ac724dc13965d33a3210a0000000000000000000000009443c07da089ef5ce930f9309d4fb07eb305307e000000000000000000000000b32b4061a8db2669096b52550cf66f7c357cbb480000000000000000000000002425f8701b84b05ec14cb340dbd0d379859a1844000000000000000000000000b1688708eea5514389fa356192a69e265437a8570000000000000000000000002b95012e6ff1b744284baa0de7621407863cf8450000000000000000000000000d0b044dc7a3591d7b829204343ac824f2d71b240000000000000000000000000c0b7a099699203d68d14e5ce63dc7681efd482d000000000000000000000000bd6dbb46e7067a20c855756ec441564411d5291a85c77252e8682228425d672779ba5646b27cb9360da2fb52518f1d3e710b91778f4fb07c8d95351d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a242d0fc819f01593c00a59bac1ea7c78f2fd12704bf24f98ee03600000000085d15765ec038d31be49b00a7df21a625b0345043fbe776282fb982400000000f88f8d74c6dfdc12dd1dec7b10c393097cbf7743000000000000000000000000f45b917744fdd67286ce4514f1ee33485ed03012000000000000000000000000cb3c4140dd25a033c556b3304a8dcf39d85e836100000000000000000000000033b65b1fd3b511452e1c364ed0c7347b7a0c6b5b000000000000000000000000371df645f772664a2dab40405f44953136e3a42b0000000000000000000000006d36ce64e41d894f7cf3245036c917170c49dc0100000000000000000000000066f77f7676684d5a392dd877cebf4a412f084d430000000000000000000000002eca213093e07750bba7a34b0f02396b99bcf966000000000000000000000000ccba353e7bb57b3ab8464d3dd233aa64191f7c67a2921d6f18b7ce357317b86e92d2ae6eac35d50c39b4c9750dcb91129728ba5cce85802c5ea44c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048be2b75714f6139d001d734cb608f1d842e694e791fae5fb3b86c2500000000b5912c761b7e203c231db34d43b1a44a1ab2e53656ba7d138561935700000000f019492a5f64304d4f5daf510e41313bb24044600000000000000000000000008520b00b87894615d7d3195348f6dd103e624e450000000000000000000000001250cd583c217a7279b5e63b24fd193f319c6b0c000000000000000000000000ede5a30cff2e06721ab0a14d46e7b2140ff26d78000000000000000000000000a879cd27782f5d3b03a0f019d8e8d220853c2911000000000000000000000000b8ccfc553831747d4a04bc1a9c619157b0aca63c000000000000000000000000c253ba5f368b3433fa160e02eba55e3c21521b6a000000000000000000000000800b215eb5acee75a404532276f7a86ae6e37077000000000000000000000000e6a87e1e61a4e3362e477b39a90e5741db3e8b3c8ce3660d5c067219c92e7a6b82d6621b06160565afa3763e3b41a0059dc35d158fd4e12361265730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000648e6c13901e63343e69eb3c340e5b0652650e39a90c736af941545c000000005de9c71db3e1761a9f72b17d38b1335b07a62a3009de4b7642178b7200000000c8206228c8ef652d302f405ee942126c44e7f42b000000000000000000000000ab9df1615753586f4fdd301659ba030aae8cd05a000000000000000000000000b6ccae40c9bf317d8bc1f60e6c0a1f351f4b661900000000000000000000000034e3ff17b0341f2cadaf092e850e983ec73413550000000000000000000000002cd9ea79fbde0a1d8d892459d19f736892ce140e00000000000000000000000074963105911e3b34b4953f4875b2d92267e01f4900000000000000000000000027f760621d9b156724cd6c605f28ba0361d182700000000000000000000000008542c36046b7f058ae2cb311fa467d5a05668856000000000000000000000000c1481e4c57fc695aef5f5c5c06c95261ba0d627a078f0534fdd4e975580abc5d3dbcb6512bb8c028cd25b31b5508f256f3702314d6b29d780d8c0267000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dece435fa82961501cc8c96906a8ee1a88b3f616fdb86f1a2fe30e27000000003383802155be596b90dc3a2abfaa707048235a60b43dd54f6969e60f00000000fbde552e95e194601b4bb410088bd57ce5a75852000000000000000000000000c5668524c5a84d693f78111daebebc4abab93561000000000000000000000000e2ae8f5351cbc361a3c7bb3dd80f8146d6df6b54000000000000000000000000ab8a2a50a0931403eed5bd0c852c4a2d7402403b0000000000000000000000003ea7055c5c6a794c59d1b1534f94a42276f97a0400000000000000000000000050022204a286b302673c7100ec309151fc52ba57000000000000000000000000306efd3966e8eb3534aad93a359f4230bd1287250000000000000000000000001a91b82b1817911a15ea603653f62747fa795e4000000000000000000000000094aca0080ed0cc416ab43f43669e636581d39f58b6f0fe1b6d2c87689e34e15588f65b0771f8b63b44d8f72f94bd220adb1d4d4f1fbac66ff3431455000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6dedf245e0c862f0810b3426c4312705058602e403ee13e1dc88c2d0000000063ad85747f65c161ebafc915a68aef71b37e013e9209fe4294fbc13100000000acd7316cea709e705e43756a7daf5367ed7d9f0300000000000000000000000031952970bac84138fd5e9b42c1ff5a17b3491d2c000000000000000000000000f2fbd17c1ac69352731cee4d08bf953c44afaa3d000000000000000000000000cc59b60bc5c7544e4ba3c518699f9a092aa09a4a000000000000000000000000c1092d51ee119933297c85094238dc7ac842c030000000000000000000000000bd37577c7123f72eaef6e43948e8e7737eea694d000000000000000000000000ed93b47ca71e0a279349c93a3e810c5d307b7073000000000000000000000000e8b82a371d176e414f57e43f27ff5e59e9319966000000000000000000000000eef03d51ba8adc478dd0203eb1194a5fdaa6cc438f23505f707d2556eaecbe10ccbccd7a75be272b5496de499cecbc1ac3c80566d0e8b25cf6700d5b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d86ed6a47277e4e99a7ac01b3a93d6501a63902191c1f37f4dd44120000000061bc3b4095189b310e3ff22cbb73c1715d7c2e62c1204a4dce60197a000000002f82eb78b16f0a1ebf24c06b0a3d3d06bc5d5e3000000000000000000000000001fed9347739296f7343151c9233ea3dbabd506f00000000000000000000000064ae7026e510c7070ccf8b5201b65d6a06478e1a0000000000000000000000009130263b16925535dac412473e8ed5786f41d92500000000000000000000000048eff836f945d850e3983225286f4b45f622b7410000000000000000000000002334d827f340c2454df98858cd41146e02392b17000000000000000000000000cb0cad7ec1b1c12f4f81536c2152c478ff4d98400000000000000000000000007711506648f6ce35b0534050666e8d154b57606500000000000000000000000064b46e1253604e38ca90861c2f0ba501b2f651620138e740caf1db6a6e0bfb755f60af5244d8db03f5ab841d13a6f318f551bb333debc0124c1601580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b47f87ca5f8102bbfafba3ceeba645d42912846c5461f6feb85f5360000000090954c4578c6e437d41e6b293e398f3ea2ad800f3a49f95da0e2e9730000000010f5a930908b6f558f935d659b5dea6748b6f77900000000000000000000000036b833161d5010388d25766410fd617eefcfaf7c0000000000000000000000006b58e93033d9d7667cd15c7b2302ed387092a5240000000000000000000000000d06c2683e48350526dc8c57a65bbb4187c5f23d0000000000000000000000000a693942d435386fefc4df098dcb24293854a9270000000000000000000000009fc4694473072951d32081543c50d4501bbd96560000000000000000000000003dca1a1682dfb672451f872fed7c7973f4f7f5690000000000000000000000005da2273721d1ed1ecef7315f1baa2e7190bd4a740000000000000000000000008f39814814db995b3dc7441be20c1464da5ceb60e6af161c4e3f7204a3c941412f685c0709facc09372c13234814716bac98a8067d66d34349678a1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b7de9692113171bd0da9000aa0ac1723f9c80742a961204cce6057e00000000bbc3a75eb952dc290d54b239672e3b56657451191c136d38b4ad270f00000000692a7669a82cb9595e89d35df6d49372de81a16d0000000000000000000000008d70043cf27419228558f267025a914d8cce33560000000000000000000000009545cc008add5e39272f93588ee19b38a3a0de08000000000000000000000000fa6b6b12429c6b6acc7b0a6dfe77240d5df9a225000000000000000000000000e0fb770e8c3d1859c1860d0131e74a770444741b000000000000000000000000147cb2262e7fb61c6b7b3e2ac6da7a30844b0e62000000000000000000000000e8625c6015a48f34b6fbc3017e3f5d3f3d05f26a000000000000000000000000b4d7ec3411174870a2bba25451b33556d903d151000000000000000000000000ccd975123b083c7c7a30f9670efa72691e7fc31dabe5f84b8ebf422ebc14c21afb21800e7aa58c2636226604a6981c14c784012dac9f5b3dca74063a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009f67d7063833ab74ee39894ca035bd767b3f7e73e6ba8618b7a48c3500000000d4426611a0d7a936eda6a65588271151098f5d6551987c365158e03000000000071eb65e892e661ecf09b93f1ec59e52a4bae44c000000000000000000000000cb68887175bb243eb416a879bbdd355f23ee7d760000000000000000000000002ac3286df8c4fa37e9118068175a3a621caec666000000000000000000000000cfbb3176c982af47bfff3d7ed766037d213cad25000000000000000000000000449d3b22176e0d57a0a8e462e5f4cc40061cb76e000000000000000000000000d5b5db3393b6492d582a00030cc6f3185d6b4275000000000000000000000000bdc23100a3b0124e82584206e9d03c2e2ca2290b000000000000000000000000a0546502d049747ccdfbdd357758fa262d97c52700000000000000000000000013a1dc3d7e5e7b29e3b84c498d30de40e492f2067921df3d8297d84a1382d548181b6758092f7918d0bf513a46ad164ebe49f035c722a239f8e18e53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f840f37d3412cb232753a70ffe28890c86696c6fb6fb850f123bd73700000000973d4b2ee83ae7644a794365277972775095ab507e084a19917b295c00000000ae22374ef0cc945e1b799e6a41e99169a161f052000000000000000000000000d9248c69aab5077367befa6ce658ff4ab56e3b5c000000000000000000000000b940846b1d7a6878912d8e15a5b67d1bc4825158000000000000000000000000ffa9f22ac9bdcb0da7ea63108bac601b0574db240000000000000000000000004b9de979967ecd5a579d7765b401fd725f4f945b0000000000000000000000008bed416333e74c61317f7839579c8d2bc890f8550000000000000000000000002cf5cb3fa1a9fe38e7ec886b3361d5317ae5dd29000000000000000000000000679199482ff8e606597da61e22d52b45c9195f270000000000000000000000002d267a4608d7d0611d23f27bc1efe13fa8d7bf00604b0634578b9d179db332714238174316f66b13b014ca7adeb147264eeb83244cfcb7760b4b535d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c2afa66b4bd116e3a323f30d725f62698fdcb4afd2e756c356e197200000000f6ace32727afef136d7e5a2a48e20b3037314f2031d5714a00f548120000000093bd4c612c8195627e749f77483a7522807b4c620000000000000000000000001929863e2a8f4e0a636fe0268e12c554ce50626400000000000000000000000076590732efdcd078a94bb119f756dd10560a5827000000000000000000000000bd497a742c14ec56e2c1a23c7467cd097eca202d000000000000000000000000ec86e7440abd7f2a6a612c2dfb164e7c2cc3286500000000000000000000000077f8c906ccba480a742ed149d88b1947223a5356000000000000000000000000dbec8b348a46d83bf6779b7acd30f222cc78645e00000000000000000000000024f9914da133e440ad9621349cc0bb426843766800000000000000000000000024cd07147effb9155de15714496a1375d45b6d0124c9c033c5a09d0eb66f804ff0d6d423895d23094689d4308061316b065d2e03c98e5c17742af83000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017084110fdfc301d950c1431b19470637f93d9480801531d154db97c00000000c8f7264291ed781aa552405553926c57de35d158fcb44d5b1426281b00000000a67a296445a05b5e40db3007b483af751aa77832000000000000000000000000325b4d638276f73f12d8c016243b6f6bec8064610000000000000000000000005aa3ed527fe2dc5176ed3f70d0d3f506b98c8e22000000000000000000000000a27ac5556c9c9b589e4edf421239ea607c0054470000000000000000000000008b071d4e822d3b407e3bec3cffe229457c5c1857000000000000000000000000a2d35023cd7c2574f8eda81c05eb247c20d03a5e0000000000000000000000005df31661227ec2285cae770ffe2ba6347597a442000000000000000000000000fcc54b7a1f1b2b5d9f88787a38a9924b72b694350000000000000000000000003bda880a8f634c452cefe5335462ee3a917fdf3a22becd618b67b14ce8e2ef6a4833fe052b5eab7031b9ab28d5003a45d5a7d51ccb5e7e3ab967b20a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000688af27d9441c20a55853099808d86a7a1c3d4c31cfcf058cd24b3f00000000ab23f909f51e1d57043f08337843107225e92e7709ec66107d03601300000000a090cb7302024c6a88d863109b793733a601884d0000000000000000000000001e4870762b413555cc7e4e4dd1ace2172a700851000000000000000000000000c3b6a73da7795e4ceb38622d6928ff49fcfddf2f00000000000000000000000099122a28d33f135435791c30b672bd2f58e4d77b00000000000000000000000088f1662aa7c7c22057befc109279ce266cdfec2b000000000000000000000000ae3fbf1ba42779159b9d0b3a4df06825aeee45560000000000000000000000002a3d102946b28b22df689a2d94f0d170941b30410000000000000000000000008af40d2dfbab13009b2cd310c1d60d2bea0701280000000000000000000000007a6b1b405f6d4379c8ff167c317278325c1bc749acc67e74cc81df29a35ec6598d9ce7075d04532a495f9574def67c4ec7910231b74a397c51921769000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed13d22efbc3e64eb56aac5fbeea9f172d6576775c0cb946c1250e3b000000000d6fce31a210a832ef0c8246eac7ce75aae8da554b40a658ee2bf778000000000c869c3a27cfe309a88ef71d93a1381db56e575300000000000000000000000001475c36659f8d47b220f828fe75264419f7842d00000000000000000000000007e930668743513797131b0d04116e5dd9c2be0500000000000000000000000057c76a43fc1bf913dbc58948ec8beb371b9a6052000000000000000000000000795c571b40d9672c7d55e87acd50e22bd19d4a07000000000000000000000000cb8b3b07b9cf4233e42fe47c8f60024f6723e14b000000000000000000000000d7f5e7055ae00c720ebee2146038721eab472d6b00000000000000000000000071afb4612b65120108208547f1f73c72adc1983c000000000000000000000000c870b265030d5c7833b52b565fdc1a03ea3d16483a523c5bad1f3a0f5f926302abc2710b426e6e036f0c2f38a030c609c6bf033ad4e9a45090eea6640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bb3725b7bbb297894ca5350155bdf038092c7747f24af3d987fdf6d000000009050da4dbcf22763716e5b598e5a142900b58c38e1545a572c649d5c000000002b11eb08e70ace73de950b6d0973db29cb0d1e32000000000000000000000000454c6827b60c2b1ef03a614d5b2bd02068b01f53000000000000000000000000a92ead43c73e40321d4e670dadff4009bbc9377900000000000000000000000021df6c632a7ced4e3c5dbd7c1ad7a968be1cda16000000000000000000000000177a54477ab6ef3f3019ea07e440756b9c340f3000000000000000000000000035571d6f09a692741e242173cf6d5665ac6ccc40000000000000000000000000d3e7a661dc7a0d49e4f2f675b930b6549e50644f000000000000000000000000e3aa9e73951fbf39f6ba2d5c9c025d167643c247000000000000000000000000d529ee363c61992e77180c0aec0e8e0deb3c552344d5d064c29bd91568614e75a15c8e24a0aafe526bc3aa3e7bf590676449d522c1dc6e4f6af0385200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015473c74e6cd1326e966de2e558aa76ed45d615a7ad1076eee31056500000000ce93797efd6ea10ff7d2bb0211485f1d705711779443b727f068987100000000f6e9f2356fc60912e58d570e2f215d663b920c0c0000000000000000000000008241013902ecad03b0a6116ae0b86b274dc4ad2100000000000000000000000079e6bc38f8d2d56606000729495a88368757d72f0000000000000000000000001671d9675ac93544bb8a1d5cb2a924753627e4350000000000000000000000003de4706de0c6135a246dce5bb4d9983bbe87857900000000000000000000000044bfc71f5a78b8748b41dc776ed67055bbb56f0c000000000000000000000000320a1206346fdf320d9a5762c517147a4e05d73b0000000000000000000000005dbdbe3bb5ea373cb729de6ae28d021e0184fc4b000000000000000000000000724bc175f498062cbc464378211b466197ec483b39ac834c7e9c825caef7e874d8cb5402f4037e722570de6fea091a46e91ee53fbee6ad2f6583eb5e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003664946536fd3927d60b5a56cac87b1810a2cf7a43c0b5018536217b00000000bce23a2f137aa10ae49d2b16bda92c0393323e0ad108cf621d34796b0000000073e185652dba2b1d2763d27df9262a0117e7d3340000000000000000000000006052041072e3097793e8a43388e97e3b488ecb7700000000000000000000000079bcfb3fe3fba1210c3422316c596165c2a2597b0000000000000000000000000d12cb77b54fbb11d32a26158846af63d97d0d3c000000000000000000000000f3901b0233d16f489ce67a0649466200e6f15f550000000000000000000000002c30a86b58ce9d65b969d14775a33649b999964f000000000000000000000000d5855f7433dc3430e30c167e0eb5fc6445ae1749000000000000000000000000264e5553dd4fbb20e00f1b453aa1c04c6be3ba06000000000000000000000000ebb4a15681f0b90bd8d9787e11400025dc8fb234d20da059e051ab5f0b459832277da35c56b5b320cdc5ef4e14a31e0deb1071575b73241be9674b02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cad9d85e427fd90e1140c219c37a38321991a46d4c857c0347eb494c00000000eba79c76ad27e44cee2a7c172358ea25a56d5e286166333967326d13000000009a8d276e68326247b234a36f479898304ee66e45000000000000000000000000f9d0e902dc1ff7578e3a5367d06d9437c691b81d000000000000000000000000cd8483147b888144fcb98d2ccc7a444a046e2f100000000000000000000000006d48c147bde82011e42c1c4482a6665bfd91c60100000000000000000000000056097f756c20c10a5fe38e355292556455beb822000000000000000000000000ea92a3666d9f414fd27782639391c27dce7596060000000000000000000000002287da659201071bc92a047650b104172a24c9310000000000000000000000004d6f887a628a6638566c9f58adeb5248e2696e5a000000000000000000000000738a101a0a2955472547726bf8a1506696fb834b67e0645e21422173ccd9107e6699e62091ccd75a1d1d7c21e7748718019ff52b77927534b047cd020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008923f122a173cd2ef34f7c3cb368ed00d99acc0fd63f8166d8a20779000000000bb8585b4871cb38034a2a6bcf68a7675cb21040ad18f749f2cc586400000000aa79d01a53567d09841a4e02b6d66868a4c7746c00000000000000000000000080ed3605b1b9801f353db867a3b3aa62a9b6876c0000000000000000000000000e4ea4528e1f611068c63651d287ce15e0f1aa1a000000000000000000000000140aa712cfb95a221c18204afc0bf17c9514c5420000000000000000000000007576b1613bb3640a65daad64d5dcd82b88de2434000000000000000000000000bcb45107efaeff600379fa74c01b7b708a1e583500000000000000000000000038c0466a91c28a0f826afd51d7484863f96d2e27000000000000000000000000467d151387b8907a0fa89504ccabd40c823eb33c000000000000000000000000daaeff47c073403a436b786c7e35f820ae22115abca42a5d8e443f69ce78d46d5b7a2a18437b833ea50f237b6d957f0d23149c5e72d0274bc7a5eb660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009714ba15b879190601004c269f149c0a7d56f63e13bf000001a17a340000000004ae1e547202e720e1f5b02a66ab7d07fa16454c52c91217d133b02c000000001c825714af13655bfd536967272ae4338a23ef65000000000000000000000000c61efe5ffd36054c7e33ed1ad2f06d3a44c40c1f000000000000000000000000b64dd1386ad7ad26ae20f352044e9c4b8ac03309000000000000000000000000c257ea426a54ce74ad854c2f0fee2d663ffccd07000000000000000000000000873bb64e96b99d36e6f2230c0240f46d5da3620c0000000000000000000000008095c40df7baf25fddb9850cd85532452c710d360000000000000000000000000af89f269ae4f55eccdaa5326e09064ea7d3e706000000000000000000000000aaa17754c213686ab8094d6df86f40358d1301480000000000000000000000003474e870af2c4e7e90d13d0a75e0bd22feeb1123f83c88782cdb1e0e98269e38fe9cad1f27c1363daa49b3288a609c4a706ae817f41d032cfe8664720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bb73c5b02a336349733e8114da7421e03688421feb9824687c508070000000080f0a1770cb0fa277a06ff01553b830f886eb52f02ee12605f0b523900000000dde85329b9a91262a8dc732574899912ff127f2c00000000000000000000000030176f596e3ebe7a9466037bcffd5447e142d56600000000000000000000000071897c3cdd63271337a3d6404c3d7128d632092c000000000000000000000000f02aa44efddc5a0f101ca3136660df053bd0f618000000000000000000000000523f0d16b281090939f08308a8ad6e27c19a2526000000000000000000000000a7de2c16d41c00202c18a218d052ea5719efd2340000000000000000000000004b9c9d14c22e69175fadd57ed3c87f341edb55610000000000000000000000008b22be25cbfe7e6cfb408a1366270c340449316800000000000000000000000017f018152ed14f36811e3f45816a1663fb84461400d1dd17685e191b6278eb57d3b05f104b72c85ff8f3bc13619c174df9d4b9364450881ab04035730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002012b3711d3f7532419c5d2532c8ca449abdb35f9ef22f0e8e5d4b09000000009abb253e06fe570b8cd122755c83c21b91edd274c60f2e1eccc7a7580000000000fe154e8e53e06470fb6013bf45c96345e95025000000000000000000000000998b667edb68bb051c0a757274e2912d2366261d000000000000000000000000620819464969a0493cbd7a2f18d0810190ca0a590000000000000000000000006840c119bddfb918cdba0446d33d3b52acea486500000000000000000000000040e25400f3b39858e1b55e1bf0dbb97311bc0a01000000000000000000000000ba18857c7644682aa028834c26187a3a7f20253b0000000000000000000000007f8a0548350efb74b7b3f46e63b5821f4b03b875000000000000000000000000b0e1173228a2055cae260c1aa07f46403f12c82300000000000000000000000018c7466864e9594a23cf6e49d8b9011bc30b0261e2338176a4a3fa693c28681da4052d1da80ad17a52c7ca70bbebbd204be7544432afbe521919bd0e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cb13e2429366450dbef86175c38ab5da662fb5d422ab1441930ef5d0000000008ec8d2dea85e0475ef36f48425563224268f3124034d66cf5ae317000000000e64f0c58d6802a2746631235fe721b507ec4c84800000000000000000000000053fc0861932a81411d3e4a523203344f666b9a1d0000000000000000000000003a3f30526626f70d6285e1263715ec4370f8625e0000000000000000000000008188c207ac67cc615af4780f4a9ac168d729e93200000000000000000000000098103e709bad1a44ae628a09ef71d71bfbfe5d620000000000000000000000009f68fd31a76fa5361072c979341c154f33d67b07000000000000000000000000b112945f1a87a40faae8630bda310152466a376c000000000000000000000000ba134f518572bc74b60ec1313d2ea6423ac652430000000000000000000000005cc3e706e63a403ad0c03c419fce0a4e4356381bfcbf7c23fe60eb1e84c02443e911a06869c5df79317e4625601d3e26173ebe2b6a9f43314976c638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f39ea04fddc9dc7c06f4b65d7702144f516f7f7a55fd6b753450c43d00000000601aaa6d227bc65ff521894106b3b95abae99771ca76fc229961867500000000124838218a087c1906a1531b4374e3455e906240000000000000000000000000cb80313bb32aa361be26c40ccf4c802710b38516000000000000000000000000107b934ce1d0c947209fb9507490ee50629f0c3a000000000000000000000000b724fc7198b16d24179a731a51a01c5775e084590000000000000000000000004cedfe375057d039aaa7746abf92be4399b62b2a000000000000000000000000710bf613ef13864a14746604471b7300c599693b000000000000000000000000b5d12c10d21335680a83b56da947e26b7e8b91200000000000000000000000008a18ce518ea5945a80c15c0c79bc704e4f0f14180000000000000000000000001ce2a52f3b88dc1f04bebc101858b5609834675800dcc57e64d3db56482326773e755a0330d5372bcecaeb39eb4ea21e25c1802b0922f42e8137b6160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f86942cb923b035afb1a4544d6ff2520f9dbd7d3610f9187b80c936000000006fc62d19a34a5108da5ca04d9f956535c8fa0f77c129b20dfacd5427000000002334065e4dd552184211583c6abe504efd15491c000000000000000000000000ff13615aa9b4a05270de48202e862528e91b2027000000000000000000000000fbaeda4a62f367203ef3fa75956256336bfb6b2e00000000000000000000000057120f17be355616ff08a00d0b7e9e1800b88a3500000000000000000000000089719548f851d6368393e15fb71b79493f698c09000000000000000000000000afc5a1317def933c9201677312ac7a5bcbcc030f000000000000000000000000222229480331a85bfb2c946ec9288237ba46b92f00000000000000000000000059732a73f6474c2f8d4c06674b673371b887127000000000000000000000000083cc7e025e8a360d35b8b473efb31225bfd0bc6c0067da6632d81d29d7105055e374211d90dfdb5ef19ec64b04df5d0bf0e493107023714a45dc22190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005918492f6e710f0410ae8b31da58602df94d27362218e4427a36203c000000002dc3980b4048cd4e74a3373b3448dd3c1a4cee01f4ca4959d0fa294a000000000a45b30fd4cb333a90034a7d956fb62ab1f1d94f000000000000000000000000c7c6f3496d2ba30b8601c60e34df997922d00c5d0000000000000000000000002e66ef7e2b1969737ceda95d36abb878c127d01f0000000000000000000000003d24a5324f04f1756dc8ca0d7e2dca6d2034845c000000000000000000000000145dff61a21262654427700eb6c0e91747c8bd75000000000000000000000000110d7c62749b9450cea88c265d1d600579885f3800000000000000000000000061c91d44e8e898538d28950dad89d32a44851e09000000000000000000000000c1613f465aeaab07a6470e69f00e5573002c495c000000000000000000000000e3f9a63b892e4f29536b54393e733d568004e3464e8d7523c36b0e61f9cf445bee7bb77aa6f1162577899d252162f805ea2f2b008fbdac30b2126d43000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b83b127589b24c46eca6a11e63cd1939b1a8ee704e1720313560204a000000008416ac6ab7289b38e11b124b6498e60798b02776c31c011f0bd4a12e0000000092659d4ae54ee735b9d14a245e3b27177bb5b96600000000000000000000000068b42f50c45eca112573762c2f35900e776b100f000000000000000000000000d05b0c64f7ba0b7b27783744f2e69465559b9b3b000000000000000000000000725b93055ae85138c609d854b891764e17fb0c72000000000000000000000000fdca611b7509a518476e9438f9f9135b5520714700000000000000000000000015ecff76f5304544089fb974ecd88a389ff7bc240000000000000000000000003894744c4383092bf777257e3f6ba903f41a344c0000000000000000000000007dac0f42a3c78a77c792c97ecb73953fe99fae560000000000000000000000001f7018577b8854488c5a0d63991ffd1aa5f816124e39ac066cce20245b1059598f64c6583e35226658c044380b33f064841be615ef32d90c8606c8270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f030449058f882f3268176938e6940552d60a47baf5693ab670257000000000e264cc165f62205afe00f570fd22ae09b0120c550eabba175bc2616700000000e2f08718b76e194ffa2c773cf7fa531dc9fa872e000000000000000000000000f584e90762da714d93fb7b4e1ee7f44ff2fb5e4d00000000000000000000000030ca5122d758d2461276af7d08029c2cef8db032000000000000000000000000967b441ad687836d09cc570af2f404662fd5472d000000000000000000000000fdb7522c89d8874e529d723619e71c345921a43000000000000000000000000058a83302b37ba617993e9901e131d6192e7bf253000000000000000000000000fb9d33393f9a8f2b12b46a48670eb80f994432120000000000000000000000004b9011590dae9434c1f29020aa5a8369dc150768000000000000000000000000ffc13b0658cb824b54b5114c3de15370dbecd4146fa8c61c1e0e001c2008a91e4c90da2118d2de6cb2381972af8fba34506c89359243915c277f806a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c59a20d30ab112b8d47180dfea2721fc1fb1b3208149276d588e63f0000000070bac00f0e805d103364cf486078ea4c1ea9d94e6afad42d9c819e3b000000007cfdf8149e604b78c18f7b197017fc696dba91660000000000000000000000006372b139dd38861d2401bc52ac51705def2df4520000000000000000000000007adb1753cd1ff35b5bc1a2350ede5a51ea0986310000000000000000000000001c137a54ee699817d42e9509d3ae083f6fac6c12000000000000000000000000a1f7357940660a435b1a231b3d47646836c3642f0000000000000000000000000679367ba71b36119cdda4218951c11e5119145900000000000000000000000034c00f192719d8178c483e31b567581ea0ff161c000000000000000000000000ab423f6e61899801662bd04a4f90055b2705ba6400000000000000000000000066b0703674fe0d1400650c4d7ec7d343e4737c4398d7c936e499ac11c815e545c63cf071ab62ce2f72a26c61bb444b11bbd91b0b42d869302e3b6b3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097dcd906ff17f557e5fc47737597b86198d6295aa779217d5884c67800000000a7309d0405c55b5ad403d936962b676b8544e13ef70e54020a09f54200000000f798ce39e21e79057a149a7938396d496bf44c070000000000000000000000008b9e285e98f50a038c43103c3574125bed0bf15b00000000000000000000000025c55558515fb905511ddf5f11a58667b9a9ed16000000000000000000000000bccd831acf15785fde53e345883fe928d07ed13900000000000000000000000070c85f00afadef13aee93d6aa16642775b03186f000000000000000000000000aee7351a3493912be2e2644b38543f5430f4902d00000000000000000000000041715b3491036f426b23d25da71c2c763fe4354f00000000000000000000000017e64f75f151df5c6912ae2d37b6877532be69300000000000000000000000004e50db56a79677092ba7b7016c6f802ab69a25633bda9709fe9f522b29c113537e90972673c18e17e557c266bf97da42942e321f42143b7c64e7592f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000515ea8795797c1319155c54bf57ac72346dedb31b578892ad811f67500000000ec4b3372d7f0ef01823c1776556d433884835b3a92c2c9044a48111b000000008fb09b2a1f67a81d352bdc555986fb0c9eb2e773000000000000000000000000416e510869ed8419e85632150ef87c4acce71326000000000000000000000000b8bb1f380dfc705bb90b8b1c1460782985b3e5330000000000000000000000006c188f263619b96157172332b0632877675b0855000000000000000000000000038ea10206450065c9a7081f71840b1c7f955061000000000000000000000000c2b0ff6e67902d0ca2f39f2e3aeb557157aed12a000000000000000000000000c770d475c4b7510d052eea6611197f149ff47b140000000000000000000000004b1fad5e31d3a52ed3a0981eb4c348260cea925c000000000000000000000000e679381763a43a6997be8202c9b9d7770345d05ca4245b299e63bb6cdf153416a12fea7a7383ff3c4012e8247d6b151ed97ea95dfe751a3b4430593e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcfe2f65a5350c1b61a6c5011cc0d21886548f7ded00d518a702a257000000008d7d1c2aaff03d2a24424971befd4f6635667c3955510521abea2b6600000000cc2fb97eb9eeba76f703ad5d4f7350079085f22d000000000000000000000000675fdc6602727172c18389394b3fe82a1f06cb1c000000000000000000000000d183a2103625d72b3500f7712e9b483e1a7d061f0000000000000000000000009004c413d710c92408a59e36e95fba4332747c12000000000000000000000000ef71696f59757c6c3a296e439674fa5860355e3600000000000000000000000042d5e44d0a3e983e6e8f5e76f6c06f4e83859f11000000000000000000000000dc2ebd60ca2c7a73cbf6921e09196870aeff542500000000000000000000000081e04b4b5fdec86f05baaf2b4506b115d56d99000000000000000000000000000a731a144681fa0d0de4755972ebf64e2e41521d45e53b2781307201960106129d012b5994d19777e21c4b36bc40937a732e59082f36d8299971bb5d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008dedec3925b99d33e6d7ee191e74922d0263201daf9c1a6e09269e560000000091b6a41460b1ad2b94f81214a3ae4f7c8bdd830f94bdfe08d916ea0900000000848d366b4368af503890782d5642386b20f07744000000000000000000000000e531f640b06945428c537307214219597500df62000000000000000000000000d0424d601beef42807c2d91e945d490f1d0b5869000000000000000000000000ab0b3f72c6d01e2069094a17d33c073d5c3a8f3c000000000000000000000000ff13401e18be113187cfbd119e4595187637ec5100000000000000000000000006a63f49c432ab035064570c937b054c0e856837000000000000000000000000d104e70d697fba5820adc270da3283149590f478000000000000000000000000748e3e7b74a965507caf2f5b5b939d60a0da751a0000000000000000000000009333e55a89d042674a4b426a3b4e7c7a7f5403597cd2d6330eab2a278a48b50c21a14e599bc92f61c3aa0251ce72ef176d41307469f5b50107ae247e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e28dd4dabe4141df559d17d7ad1581b0061151e09ef7a6f15c8d747000000007e00475b9992a574247e82476372974606ede8139682b2597676082100000000ca4128509ce8f40278d009532ef8071b2e96b1420000000000000000000000000b40af2639cb8d3e18cff2685a40b96afbdc7401000000000000000000000000859ca516cdd17d6443c40a3eac8c9b2ea419c90c000000000000000000000000bd15983748bfb75eb97c3a5af5038e70eed46155000000000000000000000000ce44a419c9c6716dd956834001293d333c70350a000000000000000000000000459ee55a0bb3fa0f1a9ac32ae31c0377c0c164110000000000000000000000000f3bed527e6fe06ac849836ca038c91fc17228320000000000000000000000000294e92ba8c162774712d1624ddee96963acdf630000000000000000000000001d66472ac9e1d911a0ca151011d4fc26869ea01e1ae5820d5c0ea946c4233b4e96c1091dd83b7141dce0bf1179a99a5a1bacbd71d83f9544e99d254f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099313a123cec4103682ac117133517547f0a6e20cbe4b22834eba23500000000927fb96862aa0e08012aa2117012892d31d93861ba39a053bd9a103a00000000478876291854352bb925222853bd19461322a77c000000000000000000000000938edf402266c452f4056a584535835355001874000000000000000000000000d3312c74a6925a59e19e7a2391069b3848235462000000000000000000000000818a1b776559ce172486637d3f5c217386f7be0d000000000000000000000000dc290a6107cefc5fe7df6352681bd11878b3ff62000000000000000000000000ffd8434bdb8b6a3bfefc416aaf61613ae6d39e30000000000000000000000000c9888a242547d11d5d41aa3e33e9dd6854745572000000000000000000000000f469323bf082b820c2c528194e4b1a6aef99b846000000000000000000000000c9595d1348e9174a9efb06162bbbbf35b8b6064310f0ba457511200e3fad6d2576497f1f81333504d1409443304f59690f2457271e10070ecc612f44000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4df41573c6f9b3ec40682147a2b8c59fafd247d6e38bf5ee2fcbf790000000043b2047a69a537600d097a736d86e7174b7d3d583c4da94bdaaa770c00000000574a21162c29c32789878b37061a7c0c0cb2290400000000000000000000000068356d02e88a6d6c9a4f5c7b28c83e4d5b4e3a6f000000000000000000000000b5bced0ae249e41ec8d02a12d3d224154a06c90b0000000000000000000000000c70896bfe71fd0b04cfb57c9e76900085b6f954000000000000000000000000b05496382367936a4378a8042ece09794f3cec090000000000000000000000009048d60ed14c3810e32383262c1e2055d1b038460000000000000000000000008eff1611bf112a4bab5ce63691816a6fd52f5916000000000000000000000000e8d8675017e5dc61cb65ea66058c313de313cf14000000000000000000000000366337009f4b18010fe18c4471f6515775712a506c55d7631bd3cd036ec7c1567e58702b1923f30313b74d363b8b4e51532cd430aaed61746d7a21130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d53d946d6d64a6d160de93f02e7667a9b8b3254e914f32fd4b07e0000000000d6030a4a8e47560bf6a17b6e8d67315dc94c49151a4ece60ede86d30000000000450f61c5c07255e59ac6550e5466c0dfb9fc65700000000000000000000000007b9c41cf37ba73f0b3b9104f1a8f276c14c695e000000000000000000000000a5d9b8026ce6396ea7d4aa4b81130464b2c564440000000000000000000000001cdcaf2fddd4f24849d703301e31df790f44f85c00000000000000000000000061ca4c24e6a64058a666110fe637a90ef76b8b210000000000000000000000005252c95b4e34b44cada7565e45a7b052fdedfb4c0000000000000000000000003a50c8376f98711b112517062c638431979f9f4900000000000000000000000092de2547145c6b3a07c7f25c23834079431f6c5e000000000000000000000000dddd281f11ea7c3a78055b2d3901da1f8b02ee546ddcb90acf896a4dffdb3f29d7f5080b9b60062167f46e0790f6d94d575e3a79f0e6175339c19e4f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ab6a45cafe16019459ff37b4a23371725a5bc71eb679b49f6fba7000000000001fcd640c5935742300455576cce2d14c3e9c67d5eb39d39ae02cc51000000007b6f9a460c5e4139d42f1715f75b2755f21d8e5b000000000000000000000000ab7c234ecbb82c6af43bc20d5d7c036fa7f7cb340000000000000000000000002617180db836ee5aefaf6d2de511816d5fa44909000000000000000000000000e1aed935af81271ccf465415be37e9652aa6802d000000000000000000000000d20ec4735f13dc2a384cb074a1c9d010edc9160300000000000000000000000057f6ca6c68f3031aa7665b460cf6a43978781027000000000000000000000000bb8db90ffc998350e3732936ab0ddf4ac82cca7000000000000000000000000080e0d65943e5b71c075470293c535f1287b34043000000000000000000000000a36b936d5f8c6e299aa5fb1decfbb434a2a9d86018aade3d088e9f44d1f8fb28991d34586a399f36699ca53169f8d12611c21f6882a7e90822f28e6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ab6a45cafe16019459ff37b4a23371725a5bc71eb679b49f6fba7000000000001fcd640c5935742300455576cce2d14c3e9c67d5eb39d39ae02cc51000000007b6f9a460c5e4139d42f1715f75b2755f21d8e5b000000000000000000000000ab7c234ecbb82c6af43bc20d5d7c036fa7f7cb340000000000000000000000002617180db836ee5aefaf6d2de511816d5fa44909000000000000000000000000e1aed935af81271ccf465415be37e9652aa6802d000000000000000000000000d20ec4735f13dc2a384cb074a1c9d010edc9160300000000000000000000000057f6ca6c68f3031aa7665b460cf6a43978781027000000000000000000000000bb8db90ffc998350e3732936ab0ddf4ac82cca7000000000000000000000000080e0d65943e5b71c075470293c535f1287b34043000000000000000000000000a36b936d5f8c6e299aa5fb1decfbb434a2a9d86018aade3d088e9f44d1f8fb28991d34586a399f36699ca53169f8d12611c21f6882a7e90822f28e6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006179a211edbdae39b218c923f28f68206981284abf17880bca37154000000000bacfd75fb2011417fda919649358de68abb2b6439c61893ed3f9a635000000007a96db73d32f6053747d77430249d05213759920000000000000000000000000e128e9034363b60ec9542d3288c2993ee6b92742000000000000000000000000dabe6f05af8e523af0c683485bdf706f2094eb16000000000000000000000000d2b61c1b79b3230723a6d253d8cdd11728e99c410000000000000000000000005809507658854a3c8afd79564266b56ef9499965000000000000000000000000e886f578c9101d308304a140ccf21a05bd0370300000000000000000000000003e81611f44a831195858d30ac656232331d6ac4e000000000000000000000000d2ab6c12a51fbb4295f6ac3c1ea7cd4501b4b710000000000000000000000000aaca7869fc7d274f54beb415a00a7213786a410e316a676af48cac272e198a1fb19a317e25ca4a70b1c0cd6051b3d00c3719d90854d1490d4ccc3c30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af37775a89b8ab366e42ae0564103207a392c6004fe1b77bd55e681a000000005d99dc4565724f69014de27ad85ab407968f3d150753382fec2d362700000000421fc56b8ecec6000d770436e06afb5dd22ab71800000000000000000000000098845d2434c31e4730ffe15fd8b3043eef5cd4620000000000000000000000002807223a3addea305adf4b41e97cf26179690d0b00000000000000000000000024e6a97a1d4beb48e11e8575fcfabe6f0282540500000000000000000000000093826845cf917f0f33ecb3030fc28a700a8e1771000000000000000000000000053532590e70404c8e5e5545d0fa4965ca00110200000000000000000000000039282264f446b17d8909ee29e0f6a43fe7bc1e67000000000000000000000000e864c43dac25ea5078d1286b94f5fd2f3028ec40000000000000000000000000df6f9f66e232e145090ba4126f0fb4474709675feefd0e43f88f5a6ea6e6c26f52bea936e2399e04d21b7e076389cc627d1b9c12f88681748fb7d8430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e724a681c519850ff34083144ef495a3455a8217bd3e4519dac7a6400000000cc9a2335b89f61159ecd5c6218d71010eb19985e2d79be423a4367590000000001f98230949d8b3706e5114db9eaa443a13bc15300000000000000000000000046617a3bdfe78330fcb89c25153ebb2c55cc6a32000000000000000000000000c0f1f05c88cf781d754ad56dccb93f47776668540000000000000000000000009134797a51dd7e402d93ec6c6f295522838ca631000000000000000000000000895ae603474c95058283b82ffab118081537383300000000000000000000000028000b75aafc827ad929d6147447db5d7e325e4a0000000000000000000000002817e0270c74c14d97a2531cd4de4c2286b4564500000000000000000000000019de640d15647a78947d8f5efc31eb428021bd7a0000000000000000000000001dccd346f8e7db6634547f225ec3864257bb1d07e049cf490514811e0a97c34c2d6b0c334b23e26ff6196f28a684f52343d85f6de6dad821cae67725000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000266945041480fe4242cb7171154b97019340ec40c5f92c76df74d02500000000c2845b209a0b0e58f57fca1248bf5063c646b06449a5d823dc69154c00000000bc47cf3fb42d115c6ffa7d40e786436952e56c4d00000000000000000000000050ddec1edd5bf734bb4c875f0b939c0dd1d87401000000000000000000000000f5388a54c2addd0dbaa0a07b7809e40789e58b1e00000000000000000000000005f075406b86e649f0443b26fd32e20b926e3926000000000000000000000000b2685113255f5c65730c6b3d25a545014defe5510000000000000000000000005267cd27e0c6af6c22c5ad164b81777c84f46b2a0000000000000000000000002017b67ad66e532ae1d3451c4b29956c63c5895a00000000000000000000000020c6e90607b3be33795a914887fa831b516f3f700000000000000000000000002831f377ded8b852803513090caaa30713bf5b659b5bfa00cf75c96dcd653838764d892ccb7546187d17a932043d91483f4778247db27169c78e456200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070a20805de07e214ca3e16388b1ad6175050645a4365b90511a45d40000000007462c92db0236818d906d872cebbc765b4870601164a18113e726f3a00000000b0743e212ce6102e6520a016b3d442236ced442400000000000000000000000019f5ae1024ea0d7ea04cb07082e64369becd5e3f0000000000000000000000000ee22b304697531d2bd90e51187b60008cd55218000000000000000000000000b55ae113da505566c7589265b528b3274f533c6f0000000000000000000000008bd67c49b117bd0e98e77e55dcc47a5f9d54c6550000000000000000000000009bb61771e3489866d46c6073f110dd0418f9a301000000000000000000000000eb8c1c2f7edd833c5df4c4551fce4b5ddd75e1130000000000000000000000006815c90ef90c1d4398391379cf706b774c372f7c00000000000000000000000076a1a42ce8095067a4a74778225ac743ac9fdb3289ea5978d18b654356dc341b0a377d6bd63ec74076953e5b2dfd0d5d8fc0323281692843a0b5881800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083282b231366fb6d5d25a100559a7c7719000966e9d5f52178f4f1690000000059afa334541cf022af19e16a94c9620f0135171bfdbe1310c6d4b636000000002ba7eb052f174b7968330732c2628651f1e72d2f0000000000000000000000004e2162148e5ef50f6b92003ef0fb941f4906ad7a000000000000000000000000ed10cf247c0b40360855077023967168b17a424e000000000000000000000000b7db2067a151e173bd36b80954e2111ce0acff68000000000000000000000000c1701449f6d830421ac06a21b28eac2eb1de742a00000000000000000000000097f40d27a5e26233ffc639018c6a2f58c72c452400000000000000000000000029b6b93118e639783b611f2762b32e52a69a273200000000000000000000000014838f3bc59f81074b4a847085bc8732c4f4313500000000000000000000000077054d6a19c00d4c95116933a6045578a1277c5a08848a1820a9753f56c1865e62320110007cf15ec387fe0ef17fd00ae4457820d82b7215fa28e14e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d014f653fa3cad71fd82232b1a4e3616ee37994ccd4f247d554a084800000000e0ea574480ac1428c93c59048364a52d08987b64b60d2b1871fda45900000000ee834a2d219002188ef1f03aff85683c171e0b170000000000000000000000000fdd9c09753b594119de926d240ae116be82f4530000000000000000000000007ace68554d492d1b78e09b766dd1f07595533a70000000000000000000000000b673712da203f713d761002ea54afb111e3a034100000000000000000000000028af32509f93d16fde91bb315500cf25c6b0ec35000000000000000000000000eb1aff742356c04876569b21c39a534ac7c7746e000000000000000000000000920f616888a1d9565703fd5346267c501984801c0000000000000000000000007f27fd468648f17acd56653739ff4e5aa5b2881200000000000000000000000097bf5e7a87a7be18fe051c43d98da708073681105cf6b85f004e2c0a7ad0981dcdefcb64508f6e4191db6768b2675e00a923b26ee798c50aba6cb124000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7ff44269d8e02550266901e1b0da21d520f36122031233e9548fe0000000000c3288a73d6da451c0048e66ec632df1f650dd35d9304552c5974147200000000c4a6305ad80ef15967ae5219db0d7a0a4520aa15000000000000000000000000e950fb4a4474ed3185112d203acf16349248284700000000000000000000000095e7dc40837c9d2e2318b14bd5decf58fb78b823000000000000000000000000f7a4954c97b39959807c903a32821212cff0c20e0000000000000000000000003262846ae43541012de06714bf208413413db377000000000000000000000000055c4b541e1b9265d5d99565f0a1fe0909496a530000000000000000000000006760c019500fd917044e56297b4b4843ca09024300000000000000000000000051c40c72ac217707e0eb9b3bf5ae813bacff990b0000000000000000000000006a4d6e00b9bae31abb27b80c532b1074fdf6f333a13a1b539acc2569b2d128485df0ef16f410dd6e5d208f26b9fd2e3fd353e03d1e42183b1d37a92f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b574a1146b111950a484c321f6d16f55558c1172c8339f0ceee6d300000000004a8ec02ef4d25043b398b21742ebab13a38ca414bfbdee510e7d6d06000000004757ae28f906fb61602a944a6e9f697bfc17ec0900000000000000000000000035be9c1e8d9d62283e4192170a919a566ac1bf7600000000000000000000000019d8ba26e1228b4ef839dd079d6bb8105968b844000000000000000000000000e816347c09050d22997ed33c44690d029122ef32000000000000000000000000717fd252b8b1611e6592f127d37ae97286f20251000000000000000000000000bbb1ff029c8eba22b340d43f9b8e840dbacc0772000000000000000000000000574ebb36ae742459c234111c7830735f98ad5a39000000000000000000000000159c4c6208056064a02cb245bb0db16d390a5c2e00000000000000000000000031a05001e35c930ecc7bd041b508ec142168f6504b44bb3853eed7305c870c04f430b3153521750643df2523f9263a1fc071111dea554f44c1f8b623000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4c8d5683c9ccc23aa4e7d040729f03e12900d620f2ce554912ba34100000000d1f9fb0cb343e322161a092aa7923f3774a9a9353d358b2b3a32ee30000000008b01f463bb29f918ca75ad7800ea13628c09432e000000000000000000000000d4f4fc643937455871f1d26412e2ac203766ce010000000000000000000000006187220216ff81456123056964f0837b524cf915000000000000000000000000c7b2be7575f0ba31fa3ea665cca5d3219ac6b6500000000000000000000000005ebfe71a9dcef634ba173a7d44f95e483b10556a000000000000000000000000882a6e25c3cd040b1397bd3980316f2c97dc901b000000000000000000000000de7494386df85e4e4753bc17d1e4005d4b743642000000000000000000000000587e1f4648880f4c5c92125e190ea95077c74b46000000000000000000000000bdc79c09aded1479cf620e06c73ab249fcfcb018955231350d3ac67dc62be96327098164453180590cfa66364ec4a1627a544665aafc5045703c9107000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c715224e26d8cd07be9050174adb76742747f16f080bae37250f047e000000005183b4350f030528c40854556b68416d0e57af29ad1ee34aec1f7e3f0000000025c0257c714bbe7d0fe6e830c8854e2dfe87ce5d000000000000000000000000bed40c4fd848b34673c7976d7490e030e0d2355a00000000000000000000000074a69e6afd32a42862b448055263ea1ea532df7e0000000000000000000000007168250c5f57221a8eb8597bb1329e5fbc57be15000000000000000000000000850f8026a547e37dee9a00053110c367444ec45800000000000000000000000070b14e2330203d25992b5b00e950f273d475424b0000000000000000000000000dc680517444305f8a105b0344cd5a41599b0c5d0000000000000000000000002952b83822966303c922402d1982223df5b0761e000000000000000000000000c8cebf4ddfbc6c644b2c0d79d771364769717d3f2032e5502e5d1f0b8c480e4813b64d1e4766487db0c98a33fdc0dd027ebe94033d6b8c77d946ee5200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051c0ee7101ceaa6c38461932525bfc3c7550af52a1c67b3084c7fe6100000000cc9a0b1d94826d3ca595384a7d514f1924e631434c0823083a74751100000000a7ee66734be6c3244cc33419653bbe3a1eccd0310000000000000000000000002043c43196c915789d095c2bfd7a611b4a99bd2a00000000000000000000000003efe640d7712475579986783e35310ca47bfb3900000000000000000000000055aaf3202dc7e43045e5f814d0f0696230b9dd000000000000000000000000005b9714645cbc2a3051cee72112b4ac5fc1334f4e000000000000000000000000e1de6d346d938255de0759612294850e6d589551000000000000000000000000a301e748ada8164266b2620793f9402708674929000000000000000000000000ebe9bb3ce3457f7687fd84333d6892754d145f42000000000000000000000000df835b1964c2ab7bf5ef361089e2ba1681971537176267251250c82aba811a22e5742e1e1ade367d3a437573b3e7c35f7636bc04ec34905e10470600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de79df4031cda426cdf82f3ccf6b6f45394a3172f4d75559c0eb40220000000014ac92695de75b18afa54670fb3cdf3343d9fc0478fb3d1f58c5454700000000099fd43a0013ab5531706861b2d1cf2185aed81a000000000000000000000000f09cb32272ac1d356bd29a5766def6297e507469000000000000000000000000797a3a380281092c751d11765b2a47419cca322c00000000000000000000000053d17632102c01115b63d528aeda1d653ea90e500000000000000000000000004b85ca7485dcf4371ca0ae476d2e837d5729a4600000000000000000000000000dbba74a77116e4c2bab2537f8c7ea29f2d04a000000000000000000000000009781117d0ab800186d3b95670ae55d77d3145e1b0000000000000000000000002e5e0c21337ec678f30d92222d356049f4854267000000000000000000000000b5fcb84453a529324887450269f89d48d9d8396ab02af21b19401c10c8aad5316c135d7330b8a35d759a80793e4faf5eb4db3a436715dc7ac62c4838000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c82b9b34bd1c305f6961e475a434e2259a59b563fff6fa4e838d7c6d00000000bf431c103fac1915773fa4671d18a734ebb2406b5d315759e525e25600000000d9ce551f6760a11001074e4f360c9807d12b25080000000000000000000000006004922c3d33a922e6ed5b6980d0d857894c2921000000000000000000000000af6ec0474fa67362e58857225ca5a5496a74ad40000000000000000000000000d626b05724865b6e56e5d258c5b6b757cc93942500000000000000000000000060b0227e9ab1251ba020a34d4dd44f1a51cb940800000000000000000000000091c06a66c456305bfce1cf495ac4ae2c884f57330000000000000000000000007c672f44a531154f5d6b1d75bcb9ae39fce534010000000000000000000000001aab32032f95f9672da4bc5b9b51003a3e5e6e3700000000000000000000000011cb847df6ddcf073d98cc4f8be72c66dedafa101fab527ba30e4c0858c2be5b47d9967817279504ea3365116eacec6f4e2ae65930f4a4685cc72d320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009dbab937289f0d5463dcc80516c53d720278e760ea03702a07efdd42000000006898ed7d99ba146a56a9165f763a5e768de9b836b2cdac673513ea0400000000e050252492493f1ba984cb74b22deb11c37f847300000000000000000000000051d2bf2aaf11ea74922191205717084e0c4d6a740000000000000000000000004fa2291a0acf8e3e295ee52b15a0d44da237b42f0000000000000000000000008c331572b3a4816dc4d7e87d1ecee135df75285b000000000000000000000000f183475e1fbde10707dfbe31409bfd0db9e15436000000000000000000000000c1a1d22713aa7634d8f66e02b4aa5b1df87abd3d0000000000000000000000001e5048539c40eb5dfad00c0e6a394a4b68d2f4630000000000000000000000003383302f5289db7cbdc9bb7ade0b9a4ac3336b5a000000000000000000000000ede13575dd257819742fdf40c178fc409b5f3527c1aa774450d8a7045998865a9bee4a3fffb758324cae8c26897c5b3816e91a4b82f49d41e12cd90600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034a3c8500b081e68970fdc09769ede49f1ad44053532fc0b0467575a00000000d1fdda28759c0615da13a0398b3fde7b73f4301774880a4a7a59b24f000000002e33d0069bf8055097624709af552d551a07004b000000000000000000000000d1e40b7ace02a56cf29f580f6c1a6537eeaa693f000000000000000000000000e54ae23b4ff2e423722e4720d7155c0d2c2c7f65000000000000000000000000c4afd327c75d87675e47c97cf94c665c8c8d4b510000000000000000000000008a21820a8aa80405f38455347dd3546aa6a884540000000000000000000000006a47966e3bf34116b796697de093ce132be1a7750000000000000000000000008b6a505b029b375159769f1389c2d11f51383c2b000000000000000000000000f7438b3ab225455cc74ba146200dc678649f3a1a00000000000000000000000030dbea13345de421fe97825347adac37172ec876419ba570fc9d4a50382bd570102744158b459b34f5b4346e0ae39b30216a1013b84af26464862916000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028a2f32d3b10e3ec317572676e21057e2a1192b16c3f57e4f83207300000000915f3f684f1bea1afafe7218e3bc8348843b5b5aaa600509d045974a0000000076b51375394f7e0f5dad03614ee2c0638c1160580000000000000000000000002f384e4c7b264664d6d95b6486c41e705960b23b000000000000000000000000db54896e7609fe6a4ee790607c9f914a00c38e770000000000000000000000003302eb5c21879b632004764d76a15a450ccfed2e00000000000000000000000050c7142d3906ee08b70d7c26f01c6b75ac26c029000000000000000000000000c3ccfc3ced86794328757d21aae98d09d7dd2b33000000000000000000000000fa1b1774938bd12478a5ad0c1f727d7304a9f92b000000000000000000000000c8055859f43dff3bdc85891f0f8d770c33dcdf46000000000000000000000000bd448b5133410b2600600444d3a3fd21bb8afc468af7cd4b86c088406daf6909036dcd7ef763424df48390035d078a308232c37e75e68e1a1dad4616000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000785c6e5807c3af22e6a21a73788177673a4ed13b2bdf925125a1a01100000000287b8c3a3fcae15e2c8ffd79fb5e694a50b3684d29b46f617391766700000000afd04611bb939901d6c2d12d360d7637ad9e50120000000000000000000000007a501a602a0bd451dca41647c109094220ad2704000000000000000000000000d7285072338d7464f13c3e542b26b21abd056e6000000000000000000000000067c0d61c95c724215ede9e7b7721fd6743b322080000000000000000000000009b873d2d593a453860b69b1a089990274b757646000000000000000000000000b4081c4440c486101c23cf4b155b550011784148000000000000000000000000f7d2457e7fcdd06b865885784d77b5246a265908000000000000000000000000a48199096db7f22e4c8842056d6a052dd15f26410000000000000000000000001cd94e6a2646320512732f4b399f51081d902712368efb27a205cd0e27ed8209d839f14998df4c195dcc6f5e102c141523a4c91c2b990b19cf483c050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e2bbe06fcb1ed758335c70fbae8104bb9e19664cfbff4608a97604e000000008d4c8f4476a5a37434e3a9054443f22d6d0ab4096c2742036c168d62000000008b61716f2d8d532ec3be4d5f64dd9d5db6293d3a00000000000000000000000019caa879bd7d7925a3f3e16dc6e22369d634402b0000000000000000000000000cae4f144971372c02ccec134459c5237cd3e60d0000000000000000000000007296fd1cec1cd0754908917a29449500689c04360000000000000000000000003ae660185977d314da624b4b3c5ca81b8bdf3c6400000000000000000000000073ad4e73a422393f404b903fee82e704666d947b00000000000000000000000015e44e05199cb13ed568315d0d8c26357075b943000000000000000000000000a94bf42580acac70977ba959d6cb7179bda8b808000000000000000000000000a8b32501371584252b42185f96549824498e9112d4c8496fe97eb51775f89606fccfe900f70c4c5ab8511732dc26b4201641540c815a2553efd5eb36000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000119cf73076f4062937263b1696844955296b7a2b2e28c53ddb68064200000000e8df4c164c97716ca8832f3b818965000c8c270fb29fdc24e19d967c00000000bfd2525b82563c349eb43b245e7aea705d87e668000000000000000000000000bd6e94368eda1c7e476e01098e2005138f194062000000000000000000000000fc35ef1b3118de323f64ed4acb5c172b71a0f134000000000000000000000000abda6a587986c000b12df55b17ac5326869d76550000000000000000000000000ace3973b0ea6930286b2c01a6a36d52e4122c5000000000000000000000000087414e3a40b41c37332bd7426e206e6ab5b824600000000000000000000000005ea07801b92dbc0af758053ad0ba060bc7c93b7b00000000000000000000000039e42b521699aa5c86b8060b52fc0e691573bd4600000000000000000000000083a4ea16428fe54d728d3d23b440e33a1074814242dab5745cb89564210e997a903ef6536fe4e77449791046d6c5786ef42d39238e8abf38be70494e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0d0c500404b055d7ac8690806a1b25847af1e0993b41650bc31ed410000000020e2793dac674c4eb136c06d1e19965172097b05e50e973d72d8300a00000000fc61ed372d0e9530e3086405bc09996771c36068000000000000000000000000aa0ab2071ca6231e770c9c01561de75430a4403a0000000000000000000000003397bc3b601a3e73aa89012282fbf53e571ffb6b0000000000000000000000004c5bd32d6cb15260499d37567b37df1170a6d9380000000000000000000000008781285b8fc19327cdb667577af1a67116100c4e0000000000000000000000001c66d035e905ae4e255c8759d72f9027fa0c9f440000000000000000000000000717084063660b15b1a8005f11baae228c50e100000000000000000000000000be89ac4900c6b5089cf0f6485790bc4ef7a7956d00000000000000000000000085673d6184b4ba6466e16b7ba41a726fecbbeb3b4ecf4c70a5bf9d73de3fbb4cde8f3a6a99a2140b567c4b0df3facc2dc353320983bc4a2a193a4500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077ab128c43c65102465f40c9d76997a68a57f333e019d3d9768d44b000000000cc1ad630087741d5231f80cb07a8318408a8f73c8c3d627b4dbc96f000000003863bc000acdb2157da3763961650961dd68e86700000000000000000000000030e6fa79abea987814a03a7c7f2ea73dd8b9375e00000000000000000000000002ad261c5659bf6b9429f75c9a28e1753fa64c740000000000000000000000004f7b6613e697c86d4062036c7c33f27cf2be881a00000000000000000000000064cd0a78a7070622726c4679ad0c5c723fb548050000000000000000000000006a959a7a30d5d00d1c6e065f9edbfc1e8b2213310000000000000000000000008d8b1f085f5f596576549d228147dd6f4c5594700000000000000000000000007c499e0d602350639aa0cb25a0137a28e60ffc1c000000000000000000000000705dc426819b2f269329fc43fc1075455790494a030cda4e074174048a9c38282e9b1a47ba1c240f52fc9c0ef588a27aa3e5d93acd982b4d0915b3730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bebd9575bcabe742d2ce90866be2e542088095284441d67d51de10200000000974e967639a45476d36b0a66e4dfd760268444322bf3b70e8c63444d00000000f568956a1a6a2d41f7db2276a1ee3659db43bb07000000000000000000000000147f6b49425e325f876c4b61e18c246bdec0dc47000000000000000000000000b2a54c4797cd171bc33a463d69a02922a3410213000000000000000000000000cded2f271ead5b0cc423ff7247325d0064357d5f00000000000000000000000013509f79d3becb03ee4c4240a3dea3376f15b80b00000000000000000000000059701074bef6f767b04967569334696697cb3a740000000000000000000000007e24ef51b77b524809e183022966076dbe0b1b3e00000000000000000000000037b0ef1cea53175292788b43186ccc19f4ba441600000000000000000000000043fa0c48a301c26f9c1fa1059a695325edb178084807ad068bbb0f68e5846727ef8b6147c1f6ce1f12826c672e71c8047307b00a1db4e5650c8c824a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c59a20d30ab112b8d47180dfea2721fc1fb1b3208149276d588e63f0000000070bac00f0e805d103364cf486078ea4c1ea9d94e6afad42d9c819e3b000000007cfdf8149e604b78c18f7b197017fc696dba91660000000000000000000000006372b139dd38861d2401bc52ac51705def2df4520000000000000000000000007adb1753cd1ff35b5bc1a2350ede5a51ea0986310000000000000000000000001c137a54ee699817d42e9509d3ae083f6fac6c12000000000000000000000000a1f7357940660a435b1a231b3d47646836c3642f0000000000000000000000000679367ba71b36119cdda4218951c11e5119145900000000000000000000000034c00f192719d8178c483e31b567581ea0ff161c000000000000000000000000ab423f6e61899801662bd04a4f90055b2705ba6400000000000000000000000066b0703674fe0d1400650c4d7ec7d343e4737c4398d7c936e499ac11c815e545c63cf071ab62ce2f72a26c61bb444b11bbd91b0b42d869302e3b6b35000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc5cb804c699472e35e2b9714c95f553a79a520e4ee38c6e1651c03b00000000387b12767d7871124bb8057ac243bc15ccb1d44c4a97fa3e03fa2f69000000006bce1230b54f593b6872511b45ae671e7795516f0000000000000000000000002f6078125e0a2d22ec96291cc490d4413f09cf2800000000000000000000000005939917c2c8535f21b3a466a71f4e30d1f1c17d000000000000000000000000cb467a22f174e15be563c1452e67f57254e9e82200000000000000000000000027e7fa6717949634af890e3114e06542595283500000000000000000000000002a61f42088c04523a96f265a0fb5157ed52fce37000000000000000000000000187cac0daadd514b16d8c1395fb9c123ad7c62300000000000000000000000008168f335f103cc36d7b3360b98ec6a7ec893a11d000000000000000000000000dba20d695ae02e34d68e6431f174716a274da22a73435e4f512bca262933b44cdc79120e4e86e471ecdaf22386e12774441b392eaa33653b12b54d240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004316a901320ae744fc9276228eaf1b0c476bd45d3a9a8c28f2bfa0610000000055b2d522c486857c9d3fcf2b8fadb13ba2897334612d760c0913e62f00000000eed03252717bba712872a31bc6e18a0cffd7383100000000000000000000000035c2dd58cf9db078cb0ebc40da0ed407dde8db19000000000000000000000000a174ea4b572067268c236237477fdb2ba5f25638000000000000000000000000abae85709953d7757bbbd409493b540e64de3047000000000000000000000000252e685fe1044340d8cc7e70d29c760468c1f34f000000000000000000000000b492dc0f2560927c409a013efe64063bc0f1ef6f0000000000000000000000008c5a5e3ca0e57b42e802aa1fec04f0515426113e000000000000000000000000775e8718d9b2d03346d6861f4a00750148add55a0000000000000000000000001b1f195554d86b468e843c3f9171f1458aa1a648d8c00d260451493a570567200836dd7596f19a4757dfe31f88da7e745dce58521df4ca308edb451e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cea0e3467286308f8169b742813a331ee409b5fe7335f035689a50400000000cea8d65f1a00617520ad84548488f818c19adb726d5fc054cf4423400000000042bbb7273bb85e4b3900c3199ff2014108e9c23c000000000000000000000000b5755f19e15826016da80d2bfd3093700b166657000000000000000000000000660168562629072d923a0701150c9122b232df10000000000000000000000000bfe0b56de840aa0d893b05098b53e0277c655d11000000000000000000000000b20d96773506bd6f7bce8073be9ad668112c0706000000000000000000000000ce17cf061362c20ae3ad6a66cb8eb33a0966427c000000000000000000000000c0a38412286cd060ef57e6535f4fd63537ceb449000000000000000000000000d54dc22feb5ac53cb9def10997839a3c38697d6900000000000000000000000041fcee5c451e1e7e8e6cae63b6d33852808349198280002878070365ce34787e64937c16f31303373650e16a4ac7616edd0a873b5512743310799d45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5618132b902692f8babde6078bc39425e64b37d7393114753252a3600000000146c405ec137aa5d3766a51cee63e44e4adde84bfd3cf763fec03223000000002e958f647c74427837715e31b6427b4aff044b420000000000000000000000009cdf2740453a5d7673f58e7707857007f700b21d000000000000000000000000da648f5cfa2b8b533da90230b8d50d733a3e4750000000000000000000000000fafaf376a3f1b6785d681f18632b5e37287a7065000000000000000000000000119a305a1eae664fde78965e6113284616e381100000000000000000000000004d43a5114b1975093c900e5be18c2a03766f1055000000000000000000000000fa259b29e19c4c628783da176482fd108192383b00000000000000000000000045403638ba554a15be8aad02c49466569ce108070000000000000000000000008d62116bc0f51e16dd0e8638392e835137d4da579f8454333e76f330767351224830d81f2900d93a76146a44e97536452fd53349a4426810df7ffe5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd093c63527cbb4a0d5adc031eb5ae65fc76fa2bc14e9c4dbf74950500000000612cf54f292e31075948930b0ffdf266965fbf2150370c79ee7215360000000052b15b5100b2c90162ed6006c458f35d02d4a554000000000000000000000000587c7d58d35c08673d3eca5ce39ae767923e36670000000000000000000000000376b23f405bfb37c8339419163c523d1b7e6a72000000000000000000000000c3cbc544a0fafe1619e83075d40b134345e7af60000000000000000000000000f5fe687ef862605da15fac018152c30877e5fd060000000000000000000000001bf3766aa7a0a02a904d1364d756b1790bbb8d590000000000000000000000004674920ea87fa65bf02cf51180d92a4ccccf8e2d000000000000000000000000d18c6f6d4a23805bccf39965030d4e53da72010d0000000000000000000000006817524bc52fca1ccb651440a482082209088b4cd3be82349d2dd639290a8c559dde693680d6283b914de72757b7e537bf7df305fc09d323051f602f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036004b7e5e439650b8f2d81a613f1f57fd349836125ac91f3389a5610000000045d6c16db896501d860ec714cdcb773b39c45b0dc68e9c565102522800000000d8613a6c64524d5cfd79ba5c0a30104ef04be42f000000000000000000000000fbf0e86dd50f114a69737f7e8f8225616426bf300000000000000000000000007bf72543a935ec1615e9565e6629942952c8f0550000000000000000000000003fb1130b2c86db181c04d737f48ac621d545b74a0000000000000000000000001b297f6ddf7de14ff3381c75eabfbf53301af05c00000000000000000000000015d1290ad7ec722452ebb60a40c30e065d5b921300000000000000000000000075c47102d9cb7240b775cf03d3938f2074bb1800000000000000000000000000dee9b97039df11639a76ca1fbce4db1b9b1ffb5a000000000000000000000000069d6978312e1d008c28c7731068b8222e1ebb5e633bed74bf057d3595d5ee5704974a3ae8020f2d4bf4921f6203f52550b5d06fbd944f354242c90d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022b555472965b8620bc995413741db558fd1ac61bc5a31603ea20c3f00000000b4acc42384fa0e533405192aa38c486148e163758442d410443f792e00000000fa21ca765eb54162fcff366dbf1a9074ae40b55e0000000000000000000000006ab26c79ed1d244a948fa931fefd9328e794cc65000000000000000000000000656fdc673d291c0d12d176375aa40a0a8fb26656000000000000000000000000536e4a75fd841a5c0031c21d8acfec07ccf36d530000000000000000000000000f12ae37bc99827280d6923e262231476e55a746000000000000000000000000c2f4fe1412ee5150293b5575c7655538f13d4a450000000000000000000000001dd6d617bd5e8878872b354eab75c01cf6f60c260000000000000000000000008cafc768e4248f328d24f869c046525ed234836b0000000000000000000000002a663d14f5471c4730fed65d2f388c0c96536f6f957a732e5e52c166c25e8f0f056faa337758466839095c5989445868b7e1d6776440f760b2d689130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000998b352f6706e5650ca8e30c3098a6421db050c0482933c7218363a000000004cd1cd406431392c6cfb4413b2a78833745ce6244c8f451af952e67800000000e66d401ad9fe1a2626d8007b30437f56930ec05800000000000000000000000008a9717eb0f9547098bae47dda27de236b055b4f00000000000000000000000097a40a313799da050c9b6f4b12d66253ab50c62d00000000000000000000000045b51c76e4a2d822bdf7d74e48f6a405e33777180000000000000000000000003151cc1be47beb38fd9f331f2b9dad1f8979756b000000000000000000000000e00dc54551517043c6f16826e8f00e3d83263b090000000000000000000000008a46e3193aa768685dbac828d717216871c2054e0000000000000000000000003604ec74810a066c3c9cd27a75dd5465d4c9a40e0000000000000000000000004610a147c293763d55ed664058a22d729e5c61335eef6e242357897ef23d456acdd2580ac73a7e6e557bac0512fd6e5404660d560184c1598e52e277000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c351de196530f017fa2e21362df7c54d4d852116738e4d2363dc13740000000084b4694efce29221235f0f23a985202dee45ed648e2bce53b6c41647000000004717bc4a3fa7572359d985589d800f508cb05f2a0000000000000000000000008e604c0238f5d53202493d1e8312ab73bcce210a000000000000000000000000195abb16f4bd1f63a8bb6558651e201ca931b240000000000000000000000000d7284a56fe82c27aaa0ba43403584544e2b0d26600000000000000000000000055709a2f8f20ca531647192139fb3d651b2c7c56000000000000000000000000622f2b63114b396eece9c93ee8fb1f69f5ea194c0000000000000000000000006b32fe755644d42208047156ec531f2a362442530000000000000000000000003d0f9306a63ecc7a5fecd1782bff29093543ee5c000000000000000000000000118405279a15775372bd8e3a18d7101bf893d768feac77042e77a8438d92d9103d4b444196d67d540959aa00a9895724b6ca7e1e76b46b2c9b82cc04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd59fa633dacab6d1d5fc470050cf654f05eb30c6ad4b777de95094400000000813e0458e13bd440b2726a03ff69636eadaf931d5984d1474084145b00000000b8b72240614f944d8591d513a4d3152364d1cf1600000000000000000000000034266c0e6f712c4dfe28667a2edbd0619fb2146f0000000000000000000000004cede00fcf885b5605cb0330a73f8d11af56861300000000000000000000000070fb4f0613f4843407bba04ac95e75490094111600000000000000000000000070d0000a61fa43411505431d6cd814046a9e1a74000000000000000000000000724c0a4450d41450cb54ff22a941467d624167620000000000000000000000000607721c99c61659d1289409b829f86c6ba3d214000000000000000000000000bbedca2a9985cc0544d9fb5446f97d5a8a9b4a2f000000000000000000000000791358559396ae7e7971a91da27b455d80b133514aab966fdfab614fad12763740d77a062da2b65c713469183f9fa75cf4ab3c180155784a1b2cd434000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e90e9009cc77a94fac2c93489fd1866cb65ecf771d521f49ce2d176100000000adaea10f2bd75d2accf3e36559f9bb200697746cec6c7e6708b1f44700000000fee5cd57f4b9bf34af40074c6c807f2d26b1627b0000000000000000000000008a6cd9334f7852518b4cf51d1e85e242fdc002470000000000000000000000002eeac6721699d30e818d2b10eef7776bc6223174000000000000000000000000bc0892498d1b177913530e49cef85d1b938eed6400000000000000000000000004577c38fad42d4de2eb87500cb980637f4d4823000000000000000000000000137cd96cade99024e084045d81a2982185b2001f000000000000000000000000f73b7f41aff391455b03c2328cc8202744aa785800000000000000000000000095ab643d233444007d50b76cf4e5130e43d43832000000000000000000000000017ca1095f4ca5719e0fd97de2497812dca61503bf93d75df7d5e553b63c167754eca11823b2bb5458e2d47bf1cd673db2bd290c3919491fda18ab1a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a1dfe82455919127fa4f08471bb2241061017e5e181ae61158da7b78000000008366fd2f98a3d818305b1228d039f475665b9f25a01f2f5b38701e2600000000346cd247a3b2f3187915b77955fddf03b86eea210000000000000000000000003d20a855f5158a35e1c7062fd89f214b4920ec3d00000000000000000000000013824b3d6f7d2e69698b9c6c06f1b77345b17070000000000000000000000000990f465773405f1f8873e974a22cef0b964c8e790000000000000000000000001c048c704841bf19c955a737632c9b4ba7edb01a000000000000000000000000be2a76691e58ec5339cd2e2b1a216c601ec43271000000000000000000000000697098102d9a871e34fe964392cfd829fb00035d0000000000000000000000008999f26dc8097d65f8d9666de238024bdeab8b0c00000000000000000000000077757776dae0e801d55fb61c59123c27cfc93c6713e11d691244c4677becb009e506750ec78a5e66547893336eb74c50f18c184a667136451042922e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052507d6c19909b4d7e1e256684cf7f54bde350053658fa1210067f66000000003293f4720b16d1648e0cd93a183aec04ad6aaa1c70eea13242d82865000000006823fa14aa83791948a5a0031fac581dd1044911000000000000000000000000b05b6930794c9f74056d64409955727e2fd50647000000000000000000000000c637252f70a35266e69a883d2c076060236556470000000000000000000000004b76ff76023b014c432a1d6b9f642d47812d686900000000000000000000000051b9dd50376a9d1f44d3c10144bc514e6541ea2a000000000000000000000000fc7bba19315f45553c970a04237ce86158d98536000000000000000000000000772a0f6c0f1a0a5e30703543d5994a3954c3b86b000000000000000000000000418cf748dc8d0c287405686b721b4b0821d2892200000000000000000000000007a9975cf311d5364cfb4a5cee72634e649c2140b6777b46e98e986191006b669a0d5b567fb40b74373db947926fac138208301eb84b950c1f303d3300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023095339e0b6740742e6df526dea4944c41d9671bdb0690b119f1303000000000bef0635e4d18007bdd4b52ed6215e282a0f6c45c3badf2f70882303000000004882a97aa9e68277a28f801800ade54632321a0200000000000000000000000053ff18677c693e0e3913175c537c12794e691a76000000000000000000000000c1a8104194abef0df303b72c89e09c66e70a191500000000000000000000000090fd363ab422c07decbab71e70dd6418b982a8190000000000000000000000006b311f08ecb14e71857d7f1adc6b2f68f62f5874000000000000000000000000cdbb656a16ade743041c2e6f3c936460bbcc8e39000000000000000000000000e2e0536cb10940291141fc40e9831b00a68c6411000000000000000000000000a531a4751c6d6e50d981e63ef1687b4f1f4280140000000000000000000000007c26a15f28f8c11820ce426daeba7248b59a06251b5be369aedf240049fb3c179bb36a2b13635b6cb825f374df7b6569919fb620247cb11f3431ba47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a785a85a2a097a25e342512c95f716171d8b5372432b8c1def08276800000000bbf29b2a90b2ed1af73e4b5f1df1470d00fe21559ca814721d2a601600000000e7e8d92aaea66175a3619c404443e105d995ce7b0000000000000000000000009b42f83fad13e93338bcf144e0b2cc5f8863ef440000000000000000000000003832175da5e0296ef88964537d500f6a99e4b938000000000000000000000000725b010d8434e54329992a70384cf6286152ea280000000000000000000000000682311c7f027b1855471658af6b454b4ad7040d00000000000000000000000008c97c463066694bdd07bc559de76f3d035cdd7b0000000000000000000000000e60e554637917457e9b303a46ca2a4cda711e5900000000000000000000000042f39a55d22c4801b688455998a24c4eb862c93b0000000000000000000000004974426c262837384ff9b8797fec9e3f9d695753e1db1a57a47d8e1619ff965c65d11232b442c5726377fc2fdca22e4e890245409d66ee1bea3a860a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011accf3993b85f2f9c88fc74b894536db367a16cae661f088ff79b21000000008f7a5a3a7cdd7717c681f86dbc01860b224f587da766285a7664db39000000006f40f5083c1c500d1cfb8e78b03cda5f0af70c09000000000000000000000000f55ec02145b75b48b81a534f22ff7e62c26610080000000000000000000000007511c41495f9e75eae526f64ad1c234691d7de780000000000000000000000006fa10f4c6fb5c85ce7a6e8695a0a7429db7a000a000000000000000000000000427b4075413e8f0b99d5fc0113dba76ad1c2d80d000000000000000000000000afbd64523493440bf496932283c3a605df8ee0410000000000000000000000009fdf865d48fe5a3ddb7fde01438c6e7b354431020000000000000000000000003ae84c04636cd057b02207397862914db7310330000000000000000000000000c3e803574f2f4362a6d6fb401399e15c4c36943a92f86d091c1712461b270277ee85d24ae0e7f676e517d37e7ed4d46f5836b21dd6ad0e5ae045f8420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c68ad64435a421b9d4aee73358a3277044537388bc8c624e0334d6a000000004fc2187a76644d1e4a3704470c0ecb1462da1a63683bdb51b0914c410000000093da1669187f7d4c9adac2255e937802ebb9020f000000000000000000000000265ddf4a8d509b15f3e1d90a674b8f2f8fd0cb56000000000000000000000000fc975716daa716438eaa7737d5cd431d3a8d82340000000000000000000000001390095c4141a03e9808a060b9f189767a325306000000000000000000000000155a482d33577614bc4c523bda4dfc2955486b7300000000000000000000000037841b472bab6f4c88e0cf6987661b271f42561a000000000000000000000000cea7e7436740da3a4218c60c0f491c0c24844d1e000000000000000000000000b154126071e79d23da758809a3fa173f0599355c000000000000000000000000dead4f6e72333c6af4f4d6520fcf852f2a7c0d2a87eea5656a5d873b64a286414992fb1eca38005877237b38a9514c22a435b12a2daf7218670cb473000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c7f0164733f70c41c536c028c35c7c46c9170115ad94535794a5567700000000c076f81a9427934cec58342025c44923d1de58567361047bde0f4a7e0000000024e1b642e8a737787bfb0315b471fd5d7a323012000000000000000000000000d33dd44c6505b306cbfd71164963d14f076f3e71000000000000000000000000702d3b4ee057133a2330ec5dd921c5053b429053000000000000000000000000993f3e592c0d00676878775b95e2480f228d5a1e000000000000000000000000650d83539f16732f532c317c47bb092b1ea6bb48000000000000000000000000ccb80903f55d951b09d5c4029f7c9e4b27a7f85a0000000000000000000000009ccf8f23be7fd07cf128d01ae5a8735b3f9da14000000000000000000000000046b23c755bfadc3cb22c8840983ba71325d76d4b000000000000000000000000b1512f70ee65192cd4fa220c50bc6017c9c20f3fb253c24eb6a9bd6c797ae857127bc66a08a3dd7e8c90d33ed9cc171595f1b641a6f01e63f384ab0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d57665bb45ea6152a9ab718fefdc01a7b56665c0b65004b66b3af4b000000002bbb006e75122779aa732e7e52a99d3b1ce4ce702008892dd8f09e4c00000000e16b156ffdc6083c0e9e6f4ac58b54787e5dc16c00000000000000000000000050084e2d8942997626416f70dc2664089dd5e370000000000000000000000000917a40788410881f327cf526a632d133430a4a51000000000000000000000000acf2be68c9447448662c34396bc9e70ca5894f74000000000000000000000000c729fd65cebddf73c183d1757088bd56b110f450000000000000000000000000b69aae295392294893af0a5c3daaf26933dd6b54000000000000000000000000a759463fc98a941d0103df1139654c2b24449e7c000000000000000000000000f6b92633f2c0bb4107890f614b24f003f9fb402a000000000000000000000000c19bea3feabe1b3e10e49a3cc7a2ea636737ef1ddf5bd851745c926689c7ef66ef9422704cb2cd3f56a6fe633fedd0718e465d569af8a9374e79cc4100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013657347a8b57e169b4f1c29cf22b57da9dbdd48aefc747bd9b85b1e0000000072d151310278190ab0ecdb026389a816adb9251bc53fa018b3c69f6100000000eb8b3a7be3767c56aff03c5096a17f1c2247871d00000000000000000000000042da3010c4eff15bfbfcd30fa7a00425bdf29822000000000000000000000000f5d9150dd062b46fc695c375598b3969bccf1b55000000000000000000000000dba38e1a0856fc645508ed2e411a1921d29fc02b0000000000000000000000001872bf2298ca614244d8db770683fb0e29b0196b0000000000000000000000004d64ae24ff2fae7066b67c03a0324a7bab48b10a000000000000000000000000895d2c625ed0f63270435449ee8686330dcaec550000000000000000000000007ad8a13b5605e263ebab361247be9b7c477b7c16000000000000000000000000d2471b2debead554ee9421668faf6f46130b61551380531fdab2b21f87382b387aeef00755cab46054d6036efd14e6645ad232691435473dc6237c4b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c966521e43e7af3fc2644a23d77b093c8a4be470d4373223a86f4b6d000000005fd0c75335a6c1547e00f14a911fd205f09287003787410c8d3b9d190000000056432f6f05f2e76c52b76539d4f051638a3ad35d000000000000000000000000807c31744f54a9095597725602292b49dd05120e000000000000000000000000eaddac156fc649352f464e56047c6522fb26ee1800000000000000000000000046c62914d8abc03a5d09f768656a0521ae80b47400000000000000000000000069b7af106dd8987b95019121f5a12f21087db77800000000000000000000000074b9c833d4fd7a3d0d81476e44b8725bed0919540000000000000000000000000d0bcb01829c5059670cf2016b74fe42df0ebe2d000000000000000000000000faffe349fde3691d07d4e12d38e88b74b7b64f6e0000000000000000000000005c0d3c5ccbf7723c16d209179630cf3a41975651e823af549efe427697b4294500beb6434ddb0e0620b4bd2e2531fb65fa3fbf144ef8cb375d35525d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000071f1b3011f97cb6990d76f038641a5009f7b9a26a6bc0066cec78b630000000078d322343cffdb6852f2c90a6bbd275633003f2a7f199616d7a3c4560000000055979271dbb51303220be763d978aa6c96dea94e000000000000000000000000b0e1025d0b13483e93f0e17ee8776957af3627030000000000000000000000001c7bfe2a04bbf07896ec170d06ce990efd461f7e000000000000000000000000977f3d3382e8832489355b5111008f491630a70c000000000000000000000000519a8e42f17a99177205e474b555ad7ede4ca61d000000000000000000000000f531195473896033ff48795c30ca9167b094875a00000000000000000000000070f61c0f8a8cfa2d3514ac3db57ac822c561ef3100000000000000000000000001b8853ff9f7b339824262716666fb1fc461cf470000000000000000000000009dd69626dcb17c11b426243cab8e3a4a69e14114ba0ecf150dffbb056eba712246642f1765b37451ac0ac74869a8bc5534802975f15ad705e34f860300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002688871beb04267fcb3f56bdae4693fe977dd2a3761e860210c9a1900000000dc38a314cf9c400dfa130751afb04e4fbd455b21164e1b068f2bec4000000000528f6c5d646ad465ccc74e771f1ca732bb72b11e0000000000000000000000006105511a9e0249102bdebb0ae8564379e01c0e2400000000000000000000000057c9ce1b4569af3d46b38e75b0705c04cf40066e000000000000000000000000fe027d1561d96d6dc366654be1442c46029b353c000000000000000000000000c01ae60fdb0d1e171a229b5cd590bb08711d1d7c000000000000000000000000c0e03a3fbd4ffd1f5827eb267aa5b5463b32490f000000000000000000000000aa43ac45ee3a172611ac366f720144039c948c7a000000000000000000000000165580784ccbae37f2c66f004694042081cbad150000000000000000000000005c932513bd07e8043d4b9118cba1c25cc674c67a87a17064e6af056c5896c257b14ab50781a3497e94f47556de19ce06978846211512dd2461a9a17d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a65eb310b0cde12cba775166ea2e01336d120e53b398945ddf4f426500000000a2714326c54e29715df02131056004569dca8b2b3a42fd0ef046a61f000000007c1db56f88e25168988242466ef7540b879ba84e0000000000000000000000000edfaa3eab721c4ef94f442caaca9454d85a407300000000000000000000000081443d7b4351ca785830541b6a8a013fc9551f1e0000000000000000000000000fc4116714e7124956f8b618a689d95d805f096a000000000000000000000000730ec36b8477011d11f4ee2937c0a561535df8010000000000000000000000006047ce6ea9da835044f8a254c9906b181fca4b4100000000000000000000000094a17950bfb7ac2ef682296d804b33452d57110e000000000000000000000000835ebb4730be761cc755270e454f256dc767607c0000000000000000000000006c376e00002670188dae9e2616261623e51aef4d48f5b048845ef67156302e7d5e930f70440be00f854a59417d9847102032dd5594da1175186d72180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006551a128eab6e057fdb2b706793d072696698b1f69fabf1c5e06a25f000000005c1a8b41bb94aa29998772046acdb761e92e8b4bde57e3327539994f00000000170f644025dfc3082ec34e4c08ffe531f35586680000000000000000000000008e42944b0532a56abc22974f9ead8734f8e54671000000000000000000000000f0ad98649bef720010378128b4974c4d60448a620000000000000000000000003d68722e63bb3c0e9a4b07035ca64d4605783b6b00000000000000000000000068810a5a035102036f8fc633ea4e084002e3b22b00000000000000000000000026117b615ec3f0141d665002cad1e16971ff4b0c00000000000000000000000090566d0a08ad206641ce340208f2b901df099d1f000000000000000000000000122a311803bcf178494aff5b02ca071885f8043400000000000000000000000057eb851eb115a15f6acdb210d8222a00ae97ef497d8f3a06f0db6e3203db4f1442436836ec64495bfa20fd293eac431a9915da4d4fa083072d7c0b58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000859a823ac76a255a6c220233804c6d45077b4716d968674dd4c9292d00000000d0a60b3e09cccb2ca9ca7a0ad87a9765015a2939cfcd043818d3221e000000006118c51a18ca1b423642f66cd33f740c083550230000000000000000000000002fdc9d4b6cbed43d981b3b5103ac8e0e43847806000000000000000000000000c611705d32f3a95b98186c4c82a9ec2ce93d91590000000000000000000000005e620e0bc996dc471857c273f6669b2e5ad516210000000000000000000000009ced1e13ca1fbe6203bdeb29ad94f54c082f107d000000000000000000000000781e7f3d502c133cad0f8966742ed2279917424800000000000000000000000005dd7848a733162eb6f42260707d1c4956d50243000000000000000000000000e49def65c3fb2c739d3bfd0f0b484555ba0b9623000000000000000000000000290e1314fd5e5f3458f0251010854d03424e83784b611725b71eba227f4c9957ddc6691a8624730aa5242b59739cfb3c5e043b261efd1159cc929412000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000726f6f4a9c0a613ec05b847e3963234a032213447cca84578eb60a3800000000258a8014f406ae62d817032006e97577541f502bc2fe0b20f022c57a000000000d9a0c7e95e9280869ca58426f68f261d78a8148000000000000000000000000a6dd116900c1fa3e4c63c60f61b53f3c27a24128000000000000000000000000fd19c235688a6a6006ca2443f6b8995e7504091a0000000000000000000000004987192b628b0e66a81c0f7cc974c66d86fa3a6b0000000000000000000000009332a91e436e3c2d3c94231b5b85913fc26861500000000000000000000000004055965d8eb23b57fb6aa047a706031f1e884324000000000000000000000000f91e443ce96c6b6b26f7c703f0addc5efa97d5650000000000000000000000000c9a4b5c140f11199beb7e6c956ae11c5d51707d0000000000000000000000008a141b4d96b38d05ff0ffc7db24c464dadd74005f61f9262628ad105881f0e35c6212634e62d33049fbabd6383622231c72e997b45e42329fa54f54b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011af5207bdf06a15715de963ad7cc651a7aa36257610894bc4ed4a5900000000212b2d1ab287554a471dc865bb66302e0bf7381de6956d287cc6057a000000005ad40c550a57562ada8e3357285fe41260bfaf51000000000000000000000000541c9f16f730cf079095f350b9a03230fcfbeb41000000000000000000000000f5a3293d40aedd7744994b391046390f00ab9d5b000000000000000000000000ad2f5437724c1a6c329b494f4de78f1467fc6537000000000000000000000000a490d317ab5f4356c73c66746c93961a1297fb7600000000000000000000000019d8890d04b84c633e94342592be1b46574f22280000000000000000000000001db41b672c00d4124a93667b5143b6254d5f2869000000000000000000000000595f506c678348598397e559302d2b026e7a4630000000000000000000000000bbf4a540d1aaef754def5b2ca5a1cc39b16b9446b07eec7c8355b45334c97031de5b307bef2bbc06df9fce10c0becd71a82e5d3a1e98fa4ef48a9557000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000388b5e269983075c353435767bf68361e6059f1e1eff0e012d24625e0000000087cb43357978471b254c8a6ccceefd6d47d7b178e27ba050bd8c985d00000000a1ab982ded55b8202273bb6f1598b761dd065c0e000000000000000000000000b7c3ea5d39bf825b90d850271073864b62609e1800000000000000000000000013299f179ed2953246e3d20324bd453837224b33000000000000000000000000dc2e970696e7c01498a82f3c3a718d4f09946962000000000000000000000000904545458c58647532511524d656266d95cde50a000000000000000000000000a1486703bdf99f277c0a9846a1485a53272a9742000000000000000000000000f64f5a5a9d932530ed69114a9109d207d35adb7e000000000000000000000000156c754202441822e23dee709c34aa4b98b32c2700000000000000000000000076cca3341853eb5ef3d5707dd1f102038890bd078e437e729a3546173c62d01abc35596274526d27ef97045a297afb1e42a45822744f5116dc9160550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001662681624acd0726018f062b4d3ba45974d0d426a57b64e5a268613000000007ebbc0577be5281ceed3c54636c5180f889f6d10c33953052376843f00000000077e000f6bd2f14199d6ff286e28732f663e7760000000000000000000000000f700a939c4102d4d77e5330342248f17287a1e1500000000000000000000000040f9b25c8a2c651e47dae179c5063f665c8eb8300000000000000000000000008592e75ca617f946e794ee3e3ef9fb3deda1244700000000000000000000000093a45312c6123a240d92ff3886e7142548f2a9050000000000000000000000006dc6a4334143d540de6e5b40af189d0b15a6477900000000000000000000000031322235ac1f714a22c4da697b6fe83bf5e1aa6200000000000000000000000037804555bfef8e78dea5e24296cee436fcc4c278000000000000000000000000d3853d3ce748a41cceae1a3720221e07d04a851725ee463fe936cf138d4692320180220a7461c758017d9b768c9ca11edaa20525e2106d51016bc9630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002218024fd251f84c7b4a64369255eb23abaec12f0d56c53d6ba06c6f000000008979e75e73ba3e515d6df8432891443c8c08911a7d09a75fd40fd32e00000000a3fa687850c7564416aaee00767c9a2a096a297d0000000000000000000000002fb3fb70ea19df61995cfe1b29245e15c4ef900b0000000000000000000000008ee5fc36f8174235fd75a4193af07274ef82f6390000000000000000000000004090002a23e52e6ad6fd6547c99c603e255b737a000000000000000000000000fa7fed0fd14f070f8e71523c99d3dc100b1009620000000000000000000000000198987cdfccdc26d6c3196ecdb2dd69010d782f000000000000000000000000ef7ce46681d4355a1b99f32d2a21d528b6316b61000000000000000000000000b725e72cd922613ae8fae175c1c53c5764864d4600000000000000000000000006d357013fa146439bcd8050b845034f2b2ad906d52cc60e84d8b71eaf746911cb54201ea0bf7e674571e67b23aece09cbe7a72f08afb805bf19876b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2c2ff721c2172253da3383763b5123ef9226773ae725d5eb6b770460000000081dcad56b298770e2ef33355e30c3927e2b6667eb8e61079943de47600000000c9971a4b1575f624b848452de7e9011dde5e347500000000000000000000000006e3fd63ea48f471dd25735b48481a3db83d262a000000000000000000000000fe24681b925f1b6219ab8443061f4245a59f15280000000000000000000000003636831285175b332f0b4f78eb099e22760d3468000000000000000000000000974e456d4dc3151ddd11f4707a254962f12b143d0000000000000000000000004d82e4131dd8cd2f09447073626d6a612631f626000000000000000000000000f7d81f5133b296001acc3405373e2b77a05a073600000000000000000000000012d49c67ef09641a79cfe41e13532e49f2f2d243000000000000000000000000ba73683d3aeeab083834e015c3a2d04ba7ae8876d3a66634ea70522bb22c724c9ded353ddab1a85ee3b1043b1fc4bc59cdb97a048d9ff60e97f01945000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cade461e357fb04d42918d1dd671e23bf53b7f169ee4190095925c3c0000000024783f7aa922a778ad2001339097201cb78c395a502973353756dd2300000000e97c076ffb983c06d09c1d29936acc117522a81a00000000000000000000000007ae2d0b955075629f2acb08e3b07b188a569e0a00000000000000000000000074978d543d80961540bce72e505c5f0c5af3440c0000000000000000000000002696f9758eb94a6ce59bf305378de40d8b5d7004000000000000000000000000a28d9820a084986b6adcec68319a30106e3c20530000000000000000000000009936e3639c52e26fa074ea6b726e050b30a8a0120000000000000000000000000a465548d404156c672c9b3bee63f90b04cbf37c0000000000000000000000003a2c9349dab0507448c4123209dcb71b92ae211800000000000000000000000061005a4f3f05ed7b2fa5531b1bea0e5592f80032066390035a3a37198fdcf54bd5efb93648dd9274c92662205ef6f71d01c4e05879b99c625659d23d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099bcbe3547b0074893787e224a81cf70668d4c35d94a495aadce113e0000000023babd09428b886637f0a6243887314a9a9f363fde45c805ee29190b00000000c9e1e74ad36db87e46bdcd0c9fa5ed62a7cd276800000000000000000000000044a9565913652131c7dd094d2042022e29f7ac750000000000000000000000006750b4282a16307bdb05035aa7b6940b2a27cb4600000000000000000000000079899540b346a41c784eb634774d891efff627640000000000000000000000004def5762942fa73dbd6d793a653cac0cde479d5a0000000000000000000000000ad7752de694313f35ae2139f6193460922c83430000000000000000000000004b9b7b38bce38d5f341bbb4d034c0a4bb12d2846000000000000000000000000577b242083c1e00a27beeb70a0cf493c45a1c76a000000000000000000000000fef5563db11a636550cb580b66dfca4393c4f96c5d11ca75bb60f07b10c7f32707ebfc0acc95a6223a1ef46d81dd0748f5fc7f1c7bdc33454abc275500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067c9c8245822740659d32460d28eef6048bd62551bca5921f2deb154000000009910e821ff547d5a4745757a3386dd22f0866a5c153b435ec62dba6f000000006bd4d072de3ff30cbbd86f203df8db4d5a408605000000000000000000000000f5b91136ab666b7bfec11338a1ba07323d376f440000000000000000000000001df1992750bc7d41f951d6356d171426f310c841000000000000000000000000128e00331341e4699767012cbc3af20ff1fb2f71000000000000000000000000f6f06f1074d4d0237130bc68a30d01158b69fb110000000000000000000000004ade2a31525b9005fe4eca206b829120a3e045170000000000000000000000005b545a074526b8390f8a5e0eb2869a2bc9eb7378000000000000000000000000e1915772bbad3f555cc8583f1acbaa32ba8c36630000000000000000000000000ed9b56e96ea7c74c382cc4af04bf614b4f60169a5edd8436968565053a8a82bd7a6dd331606273ebf54f3240f6b4b45bab0994ec059d05700c12225000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea67bb5a0b8b252e20e7ab34516450173678717cd974ed43d80b4212c4ff6f6108d69e788e5a1a6bae07af2a32c8be1d93fe727ec524d0565a03443058fc502c9b9566164afaa9648244841c1f6df50323b5b31115580e7a69e15a3c3d2a2376335cd55ed9d9c6382d3032098bd46c7e2efcdf5d5eff7747e3cb9a1e490030335186f849a2fb681fbf1f8c694a991a2ec169f3058f87966c503f1d4af3eee020df69a34566a0ec40cd710b7011ee201fdf0ee27a536b1211a5bffa18a3db9a7e1351582826576625fe54d5308160b252ad150c680d575e35360d154ad8b198178c29466582d8cb74da299265df222e3192d69a42fa3548722e89c256fdfc757cfa0051665cbfbe0b72e861535beb4e352c5b300b1d5cfb6756900a396da621419415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3adffc592d5367bf7cdfd33e174f77b4107aab9a47466dd9211bc58b6eafacd15ca89af807100dbd648a568a60649b6d022385a228a1e381395a4a203cb0360e2dd5e80e69c13c501166c6df002a44ad006d33076c57b2b57362e88f68471e9b43549d7935d3097e4767d01042c290564dcab9d504e2a1fc263cc5d1423c208e1967654250a517a750c423d57ca226bc72ff53615dcacdce42da62c547dce5a23c260f097dd3f64948d812ee4317a2570bf60082183df567494c4fb05156ebed3827b44122b97b836c0b6ea86343a0b85f7a871010f7b4387ee556755f371d90354c2b5323aa67eb1d560efa0fd1b37a5aa93fdf1297ce9f4d8d753b045ee77f0451f6b4392b1fa628cdf08f4631e7ed4ac01b690963369a43f1ea9c12ee3c2116dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a502b05571e92fbe46ae1ed731a21b37759bb23f87667026660da16dd071575ff06e2d20168f1271e46bf35a74c46202108bc7e645d6db365060a307a4ae1003d20dd2f484e6e6e2d663d16c14def8f6a76a2ab78452e24cd4eb2a29338b608a475f271f327e31f1034da303356cf1bfb17a768dc13f1cfd37ab5412412bfc7663a5335405ae5dfe54d3ed3b9575696775c8ae07d0d2468fa53581b5254acc3620699c16b3287c18f0d09a66a6fee1d0409351cdd3582097a4b9eea6600bc34d90f6625281e0ac19324f822fa3a9b410c246953b55b636e741686d66a402b677a42f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a791d715fe591767205b5375294a6ca2575d55f16bbd27439c54af023cde546175184686f5ffa27025da7827c95b0d2089c26976033110630e28fb153f91eae1d0fbec4462f3f0231a92c874b1a7bb80366cf0838ce9b8d0e40051d4598bccc5ea1b68634e1cf5943969a223653fb3c215be7aa7e5af0ef6a9c1b406830c32526e1ccbd62c7035a4418cf2f5a1bcd7a10dcb879771b6a226b4d9e1e1db0814a166eed6b03efc5c15137d2192358edf1117a301938eb521f5d060bfb652d8bf55f8bbe714fdf7fc12b95b1144cfc47146148fdf263cf3887070d2c3a3352d101387b45f343f5dd302ac49976780a87d321b2c1af6d9e3a175a81848a48afa2602afc9f355f5dbab711efdb7e74074ab91d5c45674e90939f762b2df2706a5ba52e19320c5c0d3c0075150f1631df546e3f34589130592724425836a16c4947037757c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5039d21f393a24b21ec78e70754ee3e15a533cfa34fec55627459e325aba953d1395f0b044d001562fa72437296b3b1966b91c0770d44e88716156de053aa6e02109a0b13c84d9b937f7584c6564861f4c29666e43f83ffd65bec69b7bf91e141d3ae9bb40149dc57464f1ce3a6e04d401d4b2284a7d322e53a4e5b55df9982077b10fba002be44f71fe8e922ee135121dfc39a400af90452a3f852a606010965fe1a80d3b394ca52f3f8b7365e7429911b7821d760f63c1725f055e5391d8c216b003833e16da0c677bc6340892a1c264c75c6f62ef0bbf4bde3c102d43cf4104ef53db265aafed23b7e40a614abb1b072672b5778dbe5e6eb551a46aa33b0b021c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a6b47d6c1043d36ea9c5616223cff1297dbce277777f36424f42406e54e0aa0322384b60937755296fd0ba71ecb17874b1918e7b28f16a736b66510a631d352b31b10c779924ec6800d82340daf42c74409233077aa4db19425d614cfdf4d5281c1c541d9c46b12b29a79d7dcd98231d8be10946dd73a7029b3bc51f33ef8e139161761ce6cff47afff0280d7591890d5c6bb7151e1c6a1c1d76cf17e806a750f830e0157413a047d4a0e204eeac024eb1fdfb08b88c2926844a8666e6da3136e0f56f2c861788052498726fd6a4bd3a51f2601a62fc1364c7d36f065158f97c7d08f65787d9d6381126c207e43c5e22a2b86376d1fa096bae03f343e1ddb513b1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af688da7571bdac4749d4417759a6032b9de9761a55e6cf35d4176f60dff1953992f2d04e3beb66389696ae02081f7e555fae3c05f16bed0a5221ce156aa5c73a2d68325b304bca4a475a0a5c2e9032532a7313342b54b432aacdce44823e276d0033ee432bbe907414ebf76e992f2f624e1a035353fdd43fafc63826e28bb03552804d7ad8535b7453fd122912d08e3f643f5d58d1c31a4ecfa14a4dd5175c479fe6443c22ea8a7041d53434638fd251477d56782ba10b577114870340bd2014bbe0142010bcae65c2a160016dfcb03b79c1543b2d6c04349b344a643233a91b92b0a334ee7cb94179b7bc24583ddf753973712e20dbe604ddf8df155aa3960304f8d362cd0fc412a749c15813b37e3a08f91c51302fe449ebcc136214369c2140d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505a3513760956db4ff6a9085c4e96be14cd07824fb648b95027c15202e3422146234d40668bcdd07529bbd9609d170f10b3827b5cac4a1734459c2b41fd7e9e3f793389318dc109799a952f4f53655b0b58ba49076d850e249fd29e13a44ef873cee174653c4a7e7361e1524f923c2409f7f9705dd0ebf1390758bc3d03ce266375ba044f22bace711df0982adedd31502d148b7ab07d027b29f26e57acc0321bfa68a62a7358b834afdf251adbfb1706457d3765f36a7934cdc0eb68e1fe0862c8087869b4a33e5c59929d0c8ae966442774e8236e588905ca40755e6c25ed0a25233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50ba90a94e16b90d107fcf747ecc0b8421db3ec715057b1610f6973d78fd90cb08731e5a103c32ff060730ba78f2734f0403b761470a067a2a7cde766ef926ea1fac0cf273a56e9b6972004d43e5afc55e8ddd192eb688a200e6d15c74f19b542a803b282a69e24571dc5a375d1252666a59fc8836b8a9297e5f6f4566ca3f32664d20891c5899217ba57148353e51fa685713236ffa70b921b92bf7375ce0796682e3db74c770b9236197dd1e46da6536f38f6773ddfb581168ed78203686d05b3ea73444452864750096120f1991dd629d29196e21f7c66f4bb5241c0cd38352e18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50042ce80397dea37792fce75a83fd0429dbe5fc2066bd7040327f9e2a7022dc37f730ba30af0efd122f9bb34c1d77456ad874d46e0ece7718a4452e6abc1b344ca5804a5fc034aa3f3d6d0f2d9da2024f2048b470eb798f7a0561eb69d1418708284262119c46e8487406875206d81c64360c12316e613413f10ca722a923265bbaac1016c98a0f1993937c23fef12b35902fec1904d3eb7341918543ec019944ba1c9c1dc625ca04ec90114ba9420279d5e0c15021e50971042c5f177e3141121e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5068f55e0a05dc755dea8c8f005136615388ebdd168c8d0f2fa1267a4a0e64c97a3e1969355172260a876ccb30f7f7a14123b0234e1edca051d51590775dd60a1986db5a36cc4c0349d0204e6c279935132727762530bc5b32c19c673b05dafe76837b2868647067152ee7985c82cbc4775e9471773179e6282e5cf95faa2616073ffaeb644f47e06a3ffe904173036b2d3bc72c55b7e4162078469a6e8b988a77aeb3d779bb91e31b95cc6e71e0768b2013d76170db5ea956ee809f3771506a0df2a8804e8104ae35f3ab431b56caca17895de927c7cd9c1fdf2a8d290c99814a9699331ccef5675078c4b96376d3e0244ca5725e7dcaa56fa72072242e4fbc25e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501e49b90985e72b237bd4ef1f0ab384145753e9192be24611a668b43d59a15361349edb6926c1344620035953262cd06ddc0d9857b98397240dcb81193cf73337f214600f71cf73161824240bc0fe355a2921fb102ffb890c7dadf06e4fd23b199fc6af63c837d84e94e46e1a6cd59154885fca0894184f497a497c4402c042448f75e845289fcb09da9c3e025da188166b7a1854d2513b03a251af75f01a235b6e4a726940df9b0b94d4102045585741ab011210bb9f5764288b7d350798da0cc2902265c096ff509c51b9203500fd2bf3d7313a54d33760c438bf1e30963554db80e7083314c214b8a0e31a9c470a2375a4071fb7e73e6bbb8d684335bcc158a413b26706205e19db4a665b4c6d14315baacf452498783966e7167c982177462aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a69ab2d01087989599b914573824aad21170f4e167c0da639361b5e41513d8c7b43824a1325c45f304b52025ef1ce9549b9661f2d34f64a5546e31f1afe84e17c9ad3f7571dea711aa68d495cbdf5ea25b9219c215cc8b3207c078306bc21c1725a8f7322e34113273fd8e92e8864553c2547390a3d1c3a5c70b2a62907874b5bf21c3b4f22e4315a7ac90134dd60e06d77d4b5143b2f1f4c5a481f1c8a7c74789c4cfe10008b0e67f0a755746e2d5c311cdf49050eefcf380ae9306e73b78b624d64a53281ad1500447e993d2331266985a1e0460963142523fb572a562a3c63ef53db265aafed23b7e40a614abb1b072672b5778dbe5e6eb551a46aa33b0b021c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5072292660252cfd5a86c852152e45c00804893d39f1ccfb0ae3e8ce0a8882866b95d41476199d35216af75861994d1323ab40a615aeea4c1dfbdf27077906ab1a51336932eceb0e2d93b552457d429e165e32db023a0acd1a77200d0d23b4520ab5654b2c7375b400eb73592c6baaf9494b2b6379622378546761bd17792a843aff04e74d423ace4a4ef7497025d8656a16368f4167a73d4665662a52d3c0c642d7a4766af187af79f2874d3871548c2afb5fe279d57b9e77c8fcb93307e9945aed172e49672c7029ebcce907f48ec50ada6f4205d163b71fb2c0282475316a43b7007d5767222808c954ac70815e41758bb6f570a0b80452deb77336f94a4d342e56ac0b6fc9aa182a73036ab0beaf47d0d97f4a123cdc3a8738b86dd2e047427431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a608b2904c99f1a3d4a51136f2744cb17a8de94710bd87244e09c740da4521d08a86eed56d9415a7df53e86382f31985d9a4ad85386f7d52e638328680894ee2fff978b51f6c62121ecb89a6254abcb407068a358716c443f55774c3780256434cb0abc7664353a314be27665571f39024176a66b87dc174a979a6513336acd1eaad0d2383a09e545d8e33a4ef090c9544c07fc2447dcde5c0ff7397bd105297a07688d64fba3cd3b16f12b5219da48007f189735bc73ce15616d98136b35ab2de160c55aed59981b09086b188ffcfa596eadeb273b9e790258e639790c21541dc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a5796a572b6f8535a337422740359e7b855273642c543f00fc363a38b57c0f677f7dfa2cf5efc56c399ef1494a80f25556cc916184c9ec2846606167c6f315421a7e0a6ca8238d524790d658469b0b65fcb00931b2c3bc25cdb80b1edf11d55f3903e02f1b10b527a879b84104ba1e30f7279d766d68504736394563fe41904abc0c137c6120d424ae0a457241715f25dc4515186d17692b06d2d71992fbfe40e1dcb8334bb81142471dd5449b56522bec028b6f12e8710765de070a658cbf3077e6c1097f1f1f55d2374b7baae8cc3ec7ac285f2f85c336a8a67d7d43b9ae0051027c219e7f7f50a9ded4586953bf4fe319507d0a6ed44f28379c6eed86dc6742fcef6c8818730794e6562698c224538b60ca287b5480608aaf15664443247a70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50816cfe2d2749a172edcb211f578acd6184d1562047277c0ac91a5d5434d0d92108cd407087cc4d47ff666f625672f76f56c43548ad7fd676316deb78118a0f53f9ee806875ae6532ac210e4fa1e9e32de7bce3492a8916637fc1e00f36608a14db8b8c5308759857ac38510d78d5f97bc1ff6f4340347e075f1dc02cd32123409c0d974c576ddd65bab76d505dd1933377d27f0d7e5cf75222eb5b68739a6303efd5dd773fabe377ef813c189537f72ee8f6df5079bc32553ce766372a1e063b6213e42bca182f3ed167b71affa4511659eff469c1cc0f62191aff6bf75bf743340a843391cc67266afa64201f4eb90d6ec1c407a8a60d4876944871bcf72137e1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a29cc1f77d4f3e173bb93c27341b8d90ec3165d41a1ba316db133bb16c30db8254913aa081c709a00b22625568c52d526faa0de2524149e0f054b2848dc254d50d9ad26518c13d44b8ae8b32aa8c476304ccaf823bf39f25404149d2331671f6edcd4db455365c10a1ef3e878d0a2543ea190ca6b8fbb7b40b0df8f2621edc72fdc60ba2309c3b73c0517d740ab680370d160d3125748d6383a0b8019830b2f26e0b6105b09db0450d8755b266cce2476c6147a0756079d6c3702a345e2518676e990453a67583e0dfb93db5bd1ee57437ba91a361a59c4150c1e1a2ea03c9f00e6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a6bb75704c6b3556c3f0fee7407533a02844bdb37d3a34b6e9fbbb65fab165b42bc8b245d5320395fefa6032b4183aa0c6011416a439fd0351458520867fb361905f00256a77a473d9ce9614b23b998553aed65247ece763af542b65a067e0d20ff12f2401c156150f908dd2ad6ab0e346c1bdb54b360fa7d1bfd173bcc76b74f25173e348d175f1a3c9a3704bcc69870c2d96c39ce9e986535dce752be1485430ef53b28648e9839c4845e1da3e59d40b41c9e443f29565ea5b5f54181f37075f3e17f326cdc793dd15a7b381da6fa5fc29a7d3e1455333ece942710fa4ea8714f82973f15a5c65e191c680bbd1a00237949457ccc1690461a84ac164fb4a42e82c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508fbf0a38ff0e15160ec8b33b2f1c11356b17ec5b71c6e04cbb31aa7e15d9bb7e515a25233b5f6e155aa83d13eb88065dd0fc3442584d9249c313460851f3aa1216bbaa150bf0f7585626251fa9382a74aa74d102d214ad49e75cd03e3d7fdc04d4c76d3fc79ad07d3dc3165d8cbd2178b1e8a15c00d7672f33d41e7d54000b47e343be4344faae63e1b26f605a26674a100b3410f5a2f62114f1a017312dc900c2de89374fd6fc66158be23935db803985fd5b6d70d37e1f6cd4af7e0a05627c8ca1f762aca8655bf5227b7bb17ccd6f55004e77cf1d066314db6824c21c941b1cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50eda98b3f6a6a63477e7dd83b3b23ae2904945928f042a52dff2d822555822d4d1a64a07611470c26d630d33d4117ab2e75b08f7bbce9386e7cd2b04c23e7c858a2fb042825f0215af860a951ebe5970dd56d080705f4eb19a04b031b7d08e3481b401a5c23d75434ef61dd7d265d8731862c21519c5af077f8711f0ffbe1536014a71f2f9df5a11407c75b10d55fb4132aaab93574637347cc3d7c5569772a71b0bbe74e7354da64f903e815ce05e26f12648426d863eb30fc75d84de0cf8906112d3e4e9d022912876d6345253a1679c1f2515151952e470ad0bb4883d00d4e3bd5eb407486ad474dc257596bba07608da43f709fa11902e86d025544cea82c081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50fcc190505b83795e01f7d93658e431481705d82844e26e1b1a294a588e47584730ca7d679386ce6b7303742c6a875866d71d007b9b60c3348602b87589365e0fc63a1c3ac71b0f26e649c705fa8c0d5acd5d3e19b3da446054e9e203703db54d209ca30bcd287514605f8e09da532201b4f2944a98704104516d9666b3863a37fc6a4e0b0a92b867d4a44d25f1199f6a8003813ff0ff4c0199cd2226f74e2801b120c166d0c51646f2b6ca55b625a31b32ffec764e3832514d471e2762b4c52af2a7655350aaf55dd9fb542ea8672060cb700e6bedc9c64e397d1d18b989eb7ce18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505532f7033d7c22456beb005be596682be0a4fe19ee463d198dba307742b0b22d2865701455dbac79a2eeec73a870242d3769621d5c82b627b4997d3e3aa01049af9be82a33cd174f172cb41e0e1b2651b06b1172e529ea267bb1f2205d345729bbd4b16fa34f7011e8138d24f7498450451ae64a9355a16f82f7cc05e2de3e479b298f5d5c16c86cc16c5d7a153cb006638a98641f7e3c2c8d0a085b7d22e94972b2d042c006e562933150445e1da948a612a90d4ccbbb6df653583ca840215019347d5f95f3cb0dfef3a31f77e980192d658238ab969d17a5e1f1641fd3de1733216865d0a5c37e4796bd317802da2d10f48f59a8cb010ea2b2781467237f0548111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e12cde6f9c14c9468028b36b1de50c1767cbe0189890f76b7a7d823f7e331f664d0d7e6bb299d56ee189b808de66a4477b58a8202787274dc6e9252989c90f744e4f6a4e6a1ddb5154720e42c9f76e018fe69a09bd82ce264ba1ba35552d6f3d3da6863af65f0860420ba42fcc375c0cadb4de697fcb30580c865e43c0c3b626ca59d136d18ca11c4b484816efba9d426a66b26adf76d47a33c07d4b260af20d721ed6632e586a5ce14483104fcd3e2ea7ef56463cf520761786aa063a466448ea6220126ff6057d68a71a1af9a8890bac53f23ab7a33b442f4f2b540e89bc0e28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5078480734e847c10516dcd4011f0c017e5fd3514c6838880248b2d807e7a8ea5f1cec724866eac57db968267e6585de27533ac73c8a642934c029a464e7fe2c3b32d488264b9b824a51ad58454f53d27edac0af43244e866f83959406f21f242c6aa45b404934690dce6afc149e996b54a02ac80264600d53f4a6813e2dba257ebd01a64b6e5a815b2dbd9321d4146669e0a4301cf9ecfe1f1d3b8a023bcf225d1b0f2c4f0f8cb4785f21b06ac3420e51d9853a74b3e92468c47e4144963e511cfcfa32304059786e05492e204be9a607206d7b51986d653b78a4005d45e0c37be6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a2677ab3acb74a53c40b8cf49d049b53a0d21816fee0c8450d1e4dc52e3a05f42fe66a87d408db60f560ba469cb51b020b56b9256c867f158f0764d661cf35f077918ff724ad1c0359e59d51f29e8496ef56b6d3a5b34ed60702e84083ee3c367c038b615b2b333757d81d305226a7b153057e81474726d63f5c6d4392be4926ed98a960f142b6f423a12d472fbb5b6706e786e45a57286437f1bfa1a5629d104b84af545c3d9b837cd06a75d3526453cc0d2a26ca76ac63576869a7117b5492c93cacb084830fb23eee96748e1b12a3624fb7d1da0c8b309d7bd78128240026ebbdff93209b8df6092726237a70e645aac7c270b88da415f3db7b140b02cbb5ffa1d332f00290d42c677913bc0ffa22513c90040ce317638b9a21535e4dc82112aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a32a1496198a1ed5962bcca4844768a1ebb2d083bf35b48377d793b39733b4334311c30143172c649b4c46a248458a00736a6a559a32d03354ffc533591306f571b70250882e56a263fc03a7e5332c26363b3f2290f750e20588aed72e110934e6da4762ec9cff76e051318490e5d261cda09a76428fc587a79ae75319774c46d027a483b66d93511dbcd420680dac24f0b42d765c00ddc68a9edd639ddc02f1ce31c27196a81e31b4ace1e42452a0b72923e8a24e29c2a3f26b5a033c50ee76ab4979f35ebf598189968de7c3509fc4d1a63e459a9f2cd6fde5fa9349a5e65608c29466582d8cb74da299265df222e3192d69a42fa3548722e89c256fdfc757cfa0051665cbfbe0b72e861535beb4e352c5b300b1d5cfb6756900a396da621419415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ad6559b23674935358c935738188af9713cf21257b0a2e00501c787309409bc3c87876636fda4eb29a97188077e5785075433055bcc87455026728069476ade06c221f234e4f44e2b89b7b449ff3499400ef3f627c54b5700587ce43dfd12774804e69e15f50932606b9dd525ee280a407c30ed6f6349ff46d6bcca588534d75627cd7b10df8c6c493647427734e6b603fb4db3417696da619209c13e55382701b9c7686de4e72118278c2f2ffb36d7762f0403018366f964d5954a0b70278c3b301ebb103502632b13d6a3580d77133a0cd4481f2cc6d94dc929db44f85a637c2576560d1a480966a27b95701f26c85bc2ef7c0fe50a537dd6776f06eb4b9f421a5a640e49da0065987cea66dcefbb281e76893ef4516b0498a15e4f0a603f55f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a3123d57d20fc1f3b1879f30731ce0f3b628f29751bbec31273be4475ffd2bb4ba9ddf1057929056b6cb03420cbd4791575c9122367b317349063f20261307a0d49ab721d8f0c8e6d05813f634366de2b2457f656dbf42a3e5b21d83b3395973151a8120e8f0b4628edb83f398bbba11f0751ed7ee03351622ff1522ddd348135cc511753cca76f4e56a04c495c1d421f232db651efeac65c8135d2636e5c7f2536297654a3ddad0d1cafc01c66f5a0492800206133ad51541689b96e1f681f6e837c6263e69b4b00f09f41282e1bab0fc4a8cb0fb8e7e264097f2458c528a21953f98c140d786117906f216077eafa482c44d04be32bb04ada38390205f46377115c643658d9df1267200a3fd051ab24eb92987837ea0a3dab6353707f9a094b70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e8c29919c36dcd3a014d8f6c2700de1469b7075571880e58d8ed5f3aebd0cc4e4a8769231bdd9e3238566e4d7dfff50da60b654df275070e87882902636522511f7db7117c0143798fe4854b4d01635003c356692b847d343ce38537d57b7e706989677d833b1662e07aff36a5d30659a8304327894b3232bb639041b4d64428cdf94c478fb3cb7b2415897068616b20ab7e4a73fe395a7d34eaf9029c4a2641e05cad053687116ddb307c4cb78ce823f1ede050f09d99456341f441ae4e6d6d7c9efc4c321cd1476e146f408e4d5d149d267e35f73e4c4ceec72f6ddb673935c6dfef3f9effa7047d4086558ca33f5e184dc0129198a44460575403a06c734004f8d362cd0fc412a749c15813b37e3a08f91c51302fe449ebcc136214369c2140d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b7d3135228c0812dedcfdc53dd6e656385ad803c9ad3ee16fd7c307113a25803884d5f423acffd547c2d5f11a11e121b48efdd31783a76428ad4637ab235a53a2832ea4163d7c4219f57d63edb99f3404d5e68170fa36771271899019083ff0b94d0596b914cc708a1f7f75f540fb6050c1e2c54d35eb678a3b5cc51e6ddce60e2327807e59b4c14ae76445157c53a259d76204486704c427c27cb17e2479b68d0b71c6ed9113b68b5f2404e291cfb50e877117199ce035725a1e04a0383e455002e9474a126052db9c12919a684f0284d6bd76bbdacbd575bc9401e6ff0096d7b45f343f5dd302ac49976780a87d321b2c1af6d9e3a175a81848a48afa2602afc9f355f5dbab711efdb7e74074ab91d5c45674e90939f762b2df2706a5ba52e19320c5c0d3c0075150f1631df546e3f34589130592724425836a16c4947037757c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b61ae9551ec80918a5a47b18c5ac204f1503615eb0251e302075657795e8f058171217134999a92fa357d32d75a48a78cabc0a19aee008450f09a17cb84ae47d781b6135a9fb032fddd3dc18822c616f3d60be104423c05c8a92be6f8a814429f58f1c05e3583023e428863f0bf9b03789e98a0a9b851f51f14dcd3ba1e1480bbb0c0d18f745a805cf72c85d06dabc3ec17b887660c88c451fc4fe64b594916fbfab330223382929588fdb075282876f906b562e92cf0f41c7449f4f786c790493befc2fc4a44736f4e91e301aef365977126c57b4084e506ef552274a91b03880c9a27c1a733655f2438a7d3b0fa138326c562f528980304ca13f45eb112b4fd2052b4440c87869f276874faae1037ab44d086b58451f6342334976500e9e70152d850c92ac5d2d4526305abc8764060d9f5049b98a192bbc5b2d76a294d03fab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af6592e0d512dfd71c5a0461b73ee794f440e137de6e1090c488b913f44b70d4652aa1357c4b4424d0952d64845ce014088a0b30624c3d5617fa80e552db4b76e3aa3580f79a363150988310b992d584972c8745c94319e63dd856b4a8d0ad71739b386595e76ae283d04e15b2472586b76f3556f3dd2e72a8305f67b000e076b87c340607b30da1cce4696686cff98365902ec310b1f38010c3611652fee63591a35452dfdf20a707049800f0090f002d7243a279852a624e4f59075e91ed8352a4db75b08c4c4629b46bd78f10771123ff5977316d40d5a7d4fc46e1930851d9c5c9c2a6f308d0a553cab21713a6276aee8466042c543267461294e2978f720429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa25e5f33c6851074a210ab60df796d53d71d56521917ae289f265f138f1e9d0d78ec931937cd0673250ec4583263b62ad716665809689102d384af7169deb87058ce971f0e1a7b7dd9bba63e1864ba37f69c4c48ca5e973fbe99d7216594d43ec5febb7793f9db48ae9a090a0e4f832c0e985c19d0a87652729b940081534a3687e38313ae4ad4226cb4e355903d6a4a98d52e69fa2968167041b60dc2057275ca4b0f256264f56b425f5f3bade3f74ae8cb2516067f9f29306bda721c909d054675fc129911a916638a0c23f1acdd1ab551427d93299e69342162689b05316368d36315e1bb8e5b115fc22c85dfb37380663851c388a35a612d55534c57c3692bd60471f63b685741781c372722033dee37ba533f1d1f0b13551404e1924a3361d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a682f5b41faf87c2b54d48027b9bfd027930c1f5daeb77b07a91b9f4dced8fe305213561c2ce6e276340a0e5cb2337b38d75d83750e2b71070e3445758f7ef3201ad3215e9a2c9a3ae5a27a4a92795f0dd50d6a33aa1869380e9074217e7fef1101e089060e299928fe7df925c9fea06dcea8fe2b8de46e50f055ab046861210998d12b3b017917021f09ab3f32fd6f49d3cde77c1e746a24a0c1ce2c1f5ba054b8a04817c752444144ae4b22ad2dc6458490ec707d89b953b0482f4f81c6661c90391a0d366ad0316944f87ad527d019cbee04524166ae69f8e31639f78a10785caed3765775ae00763c3066acef456150afc03d83d97962bcfeff3710452a6ce1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ac7bd0d1ecbd81576036eda25b85b33714c86d27b3577e512c76ecc5e207571724a1fd600b038fc0d489191006db2602640f31710cd27fc303d602c431cf3923a692b8c44ac395d3467e4bd4addd3395927661501ed362e51f3c48e42b197734846d5ed1cfb5f6265a1d8a14d23f42508f098656828dbbc33c7dcb44bcefd0d1f01af406783bda13ec3e11c75a171f4396978da122ba7dc496bc0b00e244c935673bd5d2ab47a9203481c143cd1cb8b720133f96e53bdb35f8a55e660039c3d087f91ca75395a21587962143d881f56470102715b32529035eaaf23263c16e6148650985d48faa71f1068265944c1d879fcc13017087f5936b3c9ad0ed209a728761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa21be94a02c3a7044ef54f2db2873637e7dcfb0cef5581257d76c23414332251f307a04d799f575b39cc7c5a8d0d1739c1fe364ac85f8a22b15e0d511cca985a9cdedb64a3a1a01b3cda4f68163c5c12d9bb054b7fd0c053eecfbe264829d8064c2b9700a46b273c56e4ec23130fbc749184ac2a5d38e31088ea5038b8d5cc0987a31b070c9d6c2ba44ca4683656084e23ca5a0c34b359646c2f861c9c596f4104c8a559f02dfe4461a1b81f9681e73e8d7adb0d51479838877eae751251ac7b7ca2a930c0ac6f081f843d5cb49dd20f4634a63e0e423c37d24f68501c8616483acfea6f932d3b1fe2f8770528ba005894dff014187a3b4bb051786beaefe81429d33c6b3c6103444d1d5466490abe196908f3334b0ac01be888a73d01026b14d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50d5b392005a992c1457d1c3104e146270e41652653eb9e7537b774f3d9928b633dac569627d1aa10635427942add92b2b92d7f91948dcb1548ae193315589dd6f42585b5945a37d664b991c07a117760f558de226e4b1f5092768e53a3659653cf99d0b2b236358642f9d956fe66d2d58f5324238b35666660f987f5ba09e95669215734e2c5764278af398210d537e4bfd83903f92dfa80a4573b923f0395469a5958c5f62af56053e879c721410c11568d9095acd574d071dedbf5ff8baf737e990453a67583e0dfb93db5bd1ee57437ba91a361a59c4150c1e1a2ea03c9f00e6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ab3242670ddbbc67b64a32a40a43bb23aac58710a79df5b0f14085b5ff74f2479b408ae5a97b8d104414f9a577d5f8e2c77a98600e8c76c346f1e405d0d52837305ad2c0fcc38b47285688a45959b6300f5ca842822c74239c4114b783261b80e23c9a57ed0c3567e10a33d3df47f5b6cb2ae1f0d2057cc1dd4184971a3bfa864539f8151f0451b293c55d2083b26ec208da4ee7599f14873f604ef70628ea207463b430ce2447a572f18f304b03bb448e2562f715cc138179f689c029fe4c139e11cac36f9f13807d013307a879853775f651f2e586fbf18edd8c802ccb36c473c772e5fef3d3764846a6a4c478a3878ca6bfb1bc8da037154943542a8c1556563ed0361bcdfa906ea7a3b4ddaf17728cccd9c29f1226e4cfee6d56962ddc62321a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50788aee75572297634b2af4107275e400ee9ab240ec70300314f96d460883c86273a7d62635db4f553b7170414ea6f941ba1f7b7881b7756c2bea80588de0762e11c1f1220de60067a03fda51233f830e30ea186a6a593a0c85b6c52da326ac3fc57a5b23d85fdd068e49406dc604230205d6d762b6076e4788185c00974e1a4589d26835bb2adf181145b63c4854804156d833008a73f43c5088916f747abc5dafb18e1111ecb16c247da37a667c7f308f67df7587cf545eacb662779cabd5750ffb003e1cbd061fd7e80c5ae3b2ba4bde33f53e3bd1391875ad42468e65b4686d1c7b704ad8e8049da5f95ddcd4f342eb8d6062ea6d2155515b8f7b99d4844f081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503041676c589559021f901c10a217ef64e3c7ba49c41900516a6a666ca00e3e0e5a07ee77ee78051e44da0e47eb5879184345916296cdc6153dff630cfb40752ec39e914b6698a44511106d13d1c6ec2f116b4b5b8011a045cdebfd5645b37c3cb76a5817e748290cc4c793408351302a0b6cbe5e59f4274aad0d3f4d0a203c337efcd63d43ea0a3c5e8e8b1abc4bbe3ec411ca640e1a52635ab65254c5fee2003f991d2806b1fe7027fba6057d22da788c2ad166e4a92c385ec7ad531cf393302dee7d6d97f60c3b38af7d3bf7720944f437fb4195cf2d5df63ec35fa50dd725a8ee8b0e2050494b0e5f23338a61f35d775ad91100b99c6a9eea85311dbbfc5c762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acaa070269cd14c2d49293629a526d9604c2e13673b700d5306140365fa68b52abea27855d0224f577222b73efd6c337423bd0b108026fc6c1a77aa75907b5941002fca074965762e6fe87a70c5e29b75a4c54925c25dd83a46892f297ef3c50680349b72d658b8074c39da6f69c0c03f4525354de934951be584da4ba343cd11d43962697cbb1018c2f67731960be716878d9c1c3ce0d258e332c2467d77e448331237136934110568172b40599c95564b18032bf9a875694f93384b97e4d41f1536ce168780714e591d52028b05693b5efcf041ecaf3f28aa1fda5276b9f46ee2298359be7df819c8f9716f98fcee097bd56031aa261511b8b45d58b3b57420762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af728b54f3e09285c01fe5c04de950b7bbf7f70001c27db5ac6b1ac5b00397116dd56875e4b468970631ca00a85f2ff183014402f55a87c05355553781089b44aa7bc906fb09035023b8c1049ef77ea11c505582c215e6241d15aa5747904b94fc7606c1c2f502a38b92f3e417df0ea3dd8a9b06d6ecae358569323106ef41d4519881a00ca5dae41f88bbd08de1ac87ab24cf8032e06d67c938a0b0cabcb24671552e337c9ef31265783cb67beed02730b67b95fdcfb2c6e8ca6b610ecb0106e1d71656082db334a0f140a1f44deb1744874582c2bf28d0633b6a44de83b4c20c52b90099474313f7e66b674c32dcf601b36a623466a436edb8f2a206d8bf151a2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507d9de053876d5b2928245d60542b38781c9e2c12f2181f7cde344d5d62c9924c67ceea36b790de753724ea27342f6142a949a02d67586e69ddad9374a9b3036f2c40b52afb467f4bc487e049553fd7567c7526429512b6274e32a65228c3d141018b457a2b77801d399e353a1a59861cb108063e8f70791a5fd9ef6b3bc0851d96a52a6a5043f7520021d87b16ab29756d518b780781ef1c2d2ebe2d65f7c7057df3696255ff31543f70b759f3a0c248cd40266fcb9d227d667dda48a7323f02af34ba64dfe9815a3fa142465c4a5062810d31402d1096347407ad15904047153090493449c2e66d65ca3155e4479b29d2bd046203c4af000c90961348c3e06b6b8576259c84fc3d8247a46fdae6673791068717a945f84d19422841358fb63cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503d5f4f483be55546c7c14c2ac7d87c38afd25a6aba50d60b4cf2413dc527fd3b1d35ff2a7b629e790115960e52447f137a7852383b25a00c2674dd2617748a5b713d703368a268721e7de82deb194c7cb9efea3b68b1d715b890c32b5b41240086b3422a27ff7b7bf47a1a1edc02534092118f0d5e6b9c17c6d1ac40119e986025276063e641dd118a5abb6932db3b10a0ccd664db6908020d6d1778a644d011dee31329e7c3d4660888e300295011567ce1722b93e1fb6fb4c3e46b5cf31936ff4bb45f4f5e4b1d91c21866cd425d34a84ef05c660cae37c488511d9e7bda4d5e78390cf6120d77cf06361387e4eb6c46fa042f57c0db1608abbb59a9218103bd193d56181be37d5f06b87582168d7dbbba590970f9dc0097eb8c19889f3d12f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aaa2c445871f62100a553611743fc35798e3a233e989b451eb928c52ff060e0162d9fc70feb053553ea6a111f2bd8246bafc80801cb1ae8132dc3ba2c4f819d0f1845845a95164e133f2d3b54475d8d687951e821f108761925666c75212aca54d2731a350aab520c5b4d28179f86916080d9971d757d391944183738e1b0ba7d5200766952715a7621d1c36a0a83672477203e148ef0b5658664ce64c80a8012fd85144f0fb876713c298608d2177602910f932e1bb8a8668cd66e12186ff62c63e630794989b965d7ede64004df19049fe6ef6af63f213f028fa058160aa53576a6333f4297002b7bc9cf679da4221f0b52bc1c670a60411995e66254a4e02b429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a52fdbf2bb778ab3eac9fd4569cca2a6504c147757fd40409ebcaf03400f53b360478a05eb4abb1306d32d92f5ae97f6eacd4cc76859f396cceba2a775928457adb6a915b659dd80d0a6fde1001d22116f80dd6769b1a5909b308b8196181a42b995159048acaef62b08b154afe1e201ed778d645e680972862652761f9ecf41e5e6d17085909ac63bbdc275d0bb56f00a7ef794ed9fde54d77988a0120f25055b23fa52c7cccbd1271731248d127a16838b3872b4c54660c8b852d456507b7579d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a506e54757dcefac37303be453f3684191b5ae6a64e7e27af28871173692970ac2d5324b95d42f1616ed9b39e24873aee78eae41d39360af13041319b09023f0d2a2b3c4158570014272b373577d1137b577e8e536ed4e53a5278c7392b48b975783be07c35d03513797fbcdc180a30461e7ed4453abcbe8f3533e0d90401b25b6c72fd8726f4ab365faeaf456743561530649dbe5ed4b9ff542c919b3b7ba67949b5f487670202fe5d0e31c07a5dd8586fe4fcda5c01b48d3ee7f6014b1a1c246a1e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505300cf23483b2c0dcbbcca21e91cd73beb396e1639150d18adb5c375f26f266bf66a2c269eb17434454a9310df14ea5a95047f15c64e7c6e27cd701a328b0c305fe2d226b264b77a207acd12cbd7ad4b291ebe609db54e1cae50ed5dc1eccf6fefcd85238584820424d00e5750bf1b330fcf797dbce9ad138c8b5947dcdd7e6647c8541b47ba164e716efb2dff4cf13cf7f5f565efc614728b633f1d64a993755f5c3d71a8cd506e89940c02c20a5c3b1bc8b63c44efe939b346950c44e9590b4558f778373f140b65f4af150e641f051a6c393c2a21b27d05e1ac029f152a6b11e9c87ee010312eb5274e42d214e50dafb45771286333604878897846fb57555a22a249c29ba54223c6654b3937ff714b4bec0b97e8565cf3982a259807fd4430efe742f8a3b60988baf1426b56dd6151167a04d9c5e65b99e085776893765f16aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a36c1b43cfe64e732b7ba0431e40c406d373df72ebda8144db2e2ea2b87f67561c9a4760c1e0e3347a33bd22e018fdf26aaaa6508f568ee4341a4de2716e5ad4f9b887d5c3e6f79026a1df30c13018c1dc3be11259280c02f92bd8b1579a4950e327ccd7ea2d0643e6ad0695cd5c203459ef20477e723e72c361f4665517d623556053460b2f1717164b2832eb7b9ee59d282c473fb35f455648640696f68d92f73737866388de76adce73315be4b81799bcd13196f0f34054eeee016f7fdef2c5b7e876f5efe87156c63bb55437307079f22b221486b4b629b3a6e517c58db39fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50432cff3c6d98d9593a6c693159b3de3611c923004a5cc91984d2156b40c6bb72a38e5229cfd8ec574a9ecd4eb7413a746b19d5141b9e6744b677403a26b5852808bf0839fcc25b708b8bb62a0623476c0f7eaf6971548111841fc35945b6710c477be861a06b6874a3b0d85ef3489313cc161d31762b7310194a202a5cca972a1d140754bac9124e0610c97c51365626f8d0960792c2530aa80e8f089fec7e1d70b6b9318e73562529e06518973dc955a4e01442238f3c148c5bd16ac9ece2649193600cbd13f36068b48416a972de4d81850d4266ffee6f515eee731ddace0587b4ad0bda761c738ff06f54e52e320e7bb89c44f7e6784bc1593936c8fb997be26c077d10d728320cf9c7797155a87b1ae8362162f6477d1e89ba1605ef83426966e9481f9e6f50f60cef04cec6c218c563630750c1067deecf1110f37dcd5db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a445d5b01b1e3435fae713348bf8bbc70285a165336a8bf6155bcc533b9dde913fddde82a34d3fa1783ac474b6203a9706f3bdc2f99f96e1c60d55d7ce0de4c398406e350167f175437724a4e3956d7572af3d662f210722f177a2f71d56ec9017e941f2ab509b14b1cb1c44104a52f477386645ebfec8024d0c5d30835c2787d40a0c3212276836635d8e524b9ea3322cdc3562c55408c110566585e25de7462ccfb7e5eea1d77581e2a9161f1603f42f4886c757caff573c1f798272750c6497462df04109c2d4c5bb40b125e070e7cc3d4636c57d7f7003fd7012203b8511bc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503327b927785402420a87664066176155aa51376a56b0e2499936b6671930c3695865e85eb23bc36d14e2e7460e153a5fd225de051d524f74d1151858ba853c083cc1e919b537977224e8f509e6628568ad939805e49681620c20cf221fc55a12577d4c2ba487b0673c08276ab37ac75990e6f9435aa53576d09aeb1ea820c1185fb03b19372c31516cc14f4a0230207e60a63954b21c702ab4fc5867fae21963dc94d64ff0c0fc49ee0d2c7b390e985e122ff116cec1de6552e02a19a9f20b0492a6960f39247547c07dbd616c4a8c2bd725fc1405bf0324eb859d65fb6dda596e49d17b02ae50214416427a13ad7e23ea315200cb7f9406a555ae476beeb06e1c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507f0eac38403b600e87dbbd1fd327ed68e3b4742903164e4e5a0bd804debc404cb8c04812af725f1c12269f0c1fdb2d12e8b2be2a5ef4a019737b5a29934bf249887e4a6165b17a0723c36c15ad91037b7d1d3108329eeb0fa5918c531d3ca264eb6b1c6e28b24a6e6085c17bc25fd10db8945049fe6b1440da30a868ced6cc2a2046602ed18c973522445e4be102dc5e7ad9f342b991e53ebf5d6a665b9d5218511405412aa54e4fe54dac0707698c49878553099bb30408efcc5971a7e5b60b77ad90087d13bc730e6f42031589735fc45bc945878557001373d300908ae32ff4665043bc0b5e1bd5e6e13042ea4b6882eec62b90a8444b2b7974661227757d25d7b53df8f30074c2e4707612449b6c6e63d80cb68ebd3498526058d4b6a52f0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5088e886583be13b735921141ba3abdc43b4eb377e98ad9423bb98b400da63763d713f3f33af39067892af75793656845b78250402e2824373b5132a419477a6180976874a07f82863dd786b25889bd27a368bbd2fa0d4ae3a4905eb79c63048799807e06437aa6040a116d7355eea6e6aaa6d793306f6847b4b5b8b6d47224f53c2324442d66429125102f3763d07277258f70170caea084149aa3e2efd3f8c03667d0e5c78abd07d5307571622d25d548aac713011c24040c9f5ff5282a67078ba9f767c51689c454cef7e05d4cdeb1ec2b6b65297332f67e8dd385476d6ba2a7c8f7f44ce1abe32fc96eb5cffbc4006fd97303c15b01d507c21f63f18e82205cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e2712157052d35062e09af4fe4d47b14daadff0eacd220189c0f5d0753eb9462361c5242718cd109e68930289c78330310603f688e074e3097cdc726c8e05a4032fe4d3cfceb2c5d63e17f60fca37676b6236909fce3823826d1d40386ff8f21ebf5b67c49eff200c5795d7242a57429018b5d1bce21b2233c96781276eaf83723940b618637be771ecbd83b2cd07e20d6c8cd5f6720ef0fc25640193e93855873737866388de76adce73315be4b81799bcd13196f0f34054eeee016f7fdef2c5b7e876f5efe87156c63bb55437307079f22b221486b4b629b3a6e517c58db39fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50eff5f73043680d66a7c8f20e45d10b23cde43d0c53b74d4f5bf3ed775c09b60a66ec052bc34e586c699c57622c42c647a606b464bf06ad7174795d4292dcdc4c4dad840280199f051bbe8d78d98b432001344800d1436a0edc132f54004a6509897d1e2ada1471727398c11e83773b47c1451d4d5d80ff551fd8952e4df16370bf8eb5090b24941ffe83bf230036781d1ece4d60e6094955f65cb165eec50e629208297a6321b003fca5950f3405c1159f56bc23c2da4d51c36255260cdbc927aeff9d3bb5e2eb171fe2df26c5c4aa09bb6824707a8648479e5cba7689792a21987a0301e14def511b9fe01f965d5c36dafca71768fa74097e49e84248abf9133110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acae7271fe0f437651267481dad9f4645636e664b82bdfb3cd5bf385e29f6c82e092ceb6674af2a347c2bd119143b957eb483853a76bd7756f8188a45a1e88a602d585c5dd4f33313bf82f7330cce90462bdfce446748071b55c59c54efe107147a23420aacd7b22abb185135cc0e6d4a3abda0451eb33d4f5108b23eeb77806aaba1d277e40ef24471ccc5671508cc3acc4e4756ddfb6b27af7a563f38804166c447756f6098253a1ec7dc55e4e7c969a6cc2e3b399e751ca6f3c778bac7ef2e1f6bf42526054640950ae575e7adeb6f7e874216ad01c54247c4f8559f0009694ffb5f48ab93674c31a2b90d1f2eea431fae770370c2df7d2aaf841e373f7b1dec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acb8c3c4260b19a066c453452a9caae7d82112617bfe9db28a7e7e96fe9f87c4eed730f2ef7aa623a952157425c0b335658c68370a2b9851e7139d74ec0c214390e44e334247c3e3f396b355614ef5253e7b64b6ff0d6080ed2dc403dbab67b5fd8562d3f6e4b863bcea67c755416c326bd55f03068112617eb643116a0461d5c7acf071a51b5242685ca8963e9584d2d500c4f0085b4772a0a49277d93751b4889ac836c71fe6d34f5dd293d98335c481a153e49be0c6e22ae5e34308e3f327845843a519b62f12273d0c30a4ef7da187341c010f21fe1008e20723a3a324650446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501895a92c569cf8127e5c1448e41854341c9a64299396c833518e8043be5ab310d1e34c30de9a1c3eda7ade05fbe39f52c91fe95bcfa4b60201f8c745cb5b34168e97591e3e6c3d251cb80973e8217d652abc51030be47a3c98cb066968c98a3e588ab854c708787b6c49a02565cd7e703f43f77631249c65fe337f21b00f572a5cf10978baccd3799522f15224ab667795664c348056fb3263d61e1d434464725af3023a02227803330436652154c7298783b33da94db25202e9470d773979098a765967aad2c2238b20f4400f897d4874a02d43d7330f5bb7b7400935f90646741c825f8069eb6e75b312384b42d13d78d81778a818e3330984983f3ac3d01c888b866efccada791f84996856da380b842376212cc0f65e6d936658e9b9bb78dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504896e440b6f508067d7760363640bb3443df881d21faff718ba55d5b4e7908304aa5c8664481825f8a201572d4513c1d9348b613b676365b352ba540a7beb3095688b641f3e51543a1ff730d28ae815e90ea296c07eb5245a4b1fb4b1a26bb7ed7c94e24508b8e119bcf5a3000affb7ac1c09f7870090321b31c34087a492c279d6f355830242d6980874d18bbbbe111df9c1d751aec3f4296cb837c8f1bcf0b618ac2275c165e11f60e3d510a921e35dcc0ff51560b7b1959e1076e7e21ad7e4ba22c7e65a95918b44fa021baa9453e0edff362cc335f5623a6d660abff77541cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504896e440b6f508067d7760363640bb3443df881d21faff718ba55d5b4e7908304aa5c8664481825f8a201572d4513c1d9348b613b676365b352ba540a7beb3095688b641f3e51543a1ff730d28ae815e90ea296c07eb5245a4b1fb4b1a26bb7ed7c94e24508b8e119bcf5a3000affb7ac1c09f7870090321b31c34087a492c279d6f355830242d6980874d18bbbbe111df9c1d751aec3f4296cb837c8f1bcf0b618ac2275c165e11f60e3d510a921e35dcc0ff51560b7b1959e1076e7e21ad7e4ba22c7e65a95918b44fa021baa9453e0edff362cc335f5623a6d660abff77541cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508adc2a14a4bedd46810add2604335946bd3074591709492c023fba2401373d0bdd0d02043c4448045175c36e7887395b019d0145e4f476069fe69b7b74f32057e141982c18bbfe40225dd56ae0846d7517b86d020402b5072355e761db72933d5842b729e58e412d99c2c30b66590f500b257e2712454f158e7b7c3e7e2d902ac8ea471a497bbe55078b4a647b5ab2355d89961c35d800016130e31cc149db7799c16b3287c18f0d09a66a6fee1d0409351cdd3582097a4b9eea6600bc34d90f6625281e0ac19324f822fa3a9b410c246953b55b636e741686d66a402b677a42f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ab751785c40296c1284592f33968c1d3f274e5d4795a177657b38c0455cc2e15345a36d46de535c41a7be14159ad8c90f1dbe3f610ef0050710e1014cd3501e23be3819104a742b233e3dd712cdc1e23b07ce982c3700fa2ff36ab7074a1d9a07a008373e1df3da6538dbce3356d75740545bf20c27e0e805d762434daf120261339c827996794b2c3ab40f7c900d471f976a36224487572604b1712ebec8e44987fb8b19f8db440ece4990090f5d7822dce4f01416cabf5949359a25aaaeb2596f89c531a1f3e734588c656ae3de354e76247a19dfb9dc4203a91a5855cb2748a30ddb5c08eaba75d946b1569579c3362aa7f12d35970816c6d76b6ab140047cb1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5cee9019e45278257d6bbe63e6137316a940810d408f141f90b5f76c9d59f64a90dd0c53b72996667cdc0a58d43c05396c9c53031bdfcd1692f7596753d9302d6128e50363e3c417994e6c7af07a7d0a4667a52a9e5075016d70ce51f61c1b653be07c35d03513797fbcdc180a30461e7ed4453abcbe8f3533e0d90401b25b6c72fd8726f4ab365faeaf456743561530649dbe5ed4b9ff542c919b3b7ba67949b5f487670202fe5d0e31c07a5dd8586fe4fcda5c01b48d3ee7f6014b1a1c246a1e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507f482e0306ed011cf3c8652ce9c4447da9249f431f773c628f043b5db6cdc031837ef8215dc9d54bf144f95d2c84652a835ea1751659962a065c410747b710593819a56eddd3503b0eabc37be1a4a00cbef4551f9983ef060a60264ab0f7de7ca39a940fcb23e12c9f3a2a5ffd969926a539c72517b3f33a28866b77be6c0568e9d10250f48a625ebd12505ee154ee58d544b05c800ee7782281f61cf91ad6366de0451b3bafdf501769fd4e2ff346665b9ce832dde7bc5c0c5234336d2d6164f3e17f326cdc793dd15a7b381da6fa5fc29a7d3e1455333ece942710fa4ea8714f82973f15a5c65e191c680bbd1a00237949457ccc1690461a84ac164fb4a42e82c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50504762008977b434f19c5f131856f46fbeb93f680829f14a87c25961cb84eb3e741e3105f098fd2de97bde28d8f7c477212a241e30f8d53df77b3c254a1ed976d0fea73337b66744e42e9f130cb6a77cafdd09406d715c657e1f63592b4c8a1d3fc3051caf7cb8358a1f1c74a34ac02fd0dccc05eb280a4e3ac69763981c0e1e115a1e11baa56b06d0eea34285819d523de8375b0b90e233a73a527a630c4a56b120c166d0c51646f2b6ca55b625a31b32ffec764e3832514d471e2762b4c52af2a7655350aaf55dd9fb542ea8672060cb700e6bedc9c64e397d1d18b989eb7ce18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5063dcd12c93f69c7ef9d5ac4094dd4f68926248359cd23d7cc8d651342d78bd0aa6f7f63d17ebc4200c75be4715f3ee09b4aac64ce1f6380d23500a195acb1811b3c4c330a6510424fa37ec267a53577df36dbf63b7e756422ab85f1e608bc2473378c12de6d1613511c7415a920b070e3aab3b581b9e906322a1031832c72f7a1dd6cc01af082817f763c1732c0fcf5fb9eb5b7e1c53a84b717bd95d8bfa1f35a22af2776955816fceb3296b33cbe05a4902bd1601c8b85fada64322089fce6c21846971431a11332a9a6605aace0d3e9a1bd244e1126c5da6a94551ee27d9082613d41f2797bd175834a8733c40de635258585c01923850c4f86d07cadafe32cc9034658f3b6f3f3d67e6692705a66307c8364c98b43364497bdc55fe85f02cc287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a0de90b195e55e32a7c55632a5c5bd31b1c9a7425bd05b5265dbdc16bf4e4b07dd041230f909389280b21aa6e100c88309e17b546c2f0a526631c2d378f4179472b93546fad436556a821b03170d6764a388d052e7013d803a579d302d76347424891c151ddfcdd69ba49a80546207d6c6fd78e3a8c284d61d7a72e444acb024f47f1b7567d2c9f24077d7e24bedd6b4250e6633e9be62d3dbfe2352cb6f97b0fbf1027180953dd791b5977712aceef40de90ad1e6860a5316a5ca11c1ecf1118e9fd874977316a4c0ccebe02aeb479682bb23030e523f516f2f18a40b416e543da00fe64fd0aee04013e583652f46c2fe4ca7261f343f95b6bd02143d46dcc07d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50bbedfc4d7bd98466642ddd2deff3f2514f1994508f45a5323e3e6417b365c306a442c91414006117d0bace4acd1dab5f5c4c29562bd2631e290a0572666863253c418164da8988393cfe5f3f6244815dd621110d5c514550df4d6d21de4d87236bbab447e7b77a0320957354c6515c308dcc1a3ead2ca12208d3ac6ed83f7e77fd50454ae00a1d151329de3ccc61f91009595d737f17b07c3cefd36c17b1534e71720a46f0dc84477d7bd33c59d2487af4c60767545e0a1931519d441c301212832aa842a50aca03932bb5005709a60395803c45dadf920f3ee758340803ff7afddffb26d99e136d2a2c592e07816112b69e8601a730323f68abfe435581686582c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e1f58c2414efb77b85e4fd69e7907b24d96516543c7c034b4a7133107084da28499a027db515ee496bb1ce00cae4716583f1d76cae942530facdc71cb17d9902b93399515146ce221e2bca62f3db7e187e51a801f492707b91edf34632995308c5867b566769e94467967d45b940c65cc6ee676017c01f489df4a22e1f3c712ae8e06d01967daa3d075d192655aace27971a5e3432e98f378fb30a622804cf31bc7c3054bd155857a6c94b560ab94875d3f71166d2438e75732f4b60d079dc1a1649526ae89dce0f3ecf8e323ea1673780904c04ec29004b69a9d47a5f010e6c4f67f045ce694b13d3f5a6264787314cf4f34d0b7a4b5c570e655d51fa47a76180edb530d3715b4a5e6b9e5dd071462d95ff7f3aa9b2351143ea5c7c452d0a0d7431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a2f1bc9229352da0543c47e661fa3e4486777931217f7cf330b7c895a137ddd2e84d5d41f0b343464afc62c28bbdc9b641231e578ca58303871efcf66c5ad2f365780de68fd5bad5d0a0dc640fb8e4e55d5c16e6f3f269718744d6c1d6540ff3e733553166f6d8d5996c8ed0069611a152cc4005bef13aa1a691cf802ae5d4e567e49fa05d2470b4e10d83d362a613e3a463fea36f5cdcc0b6cbe04189d1e3971e0663525f7ee3e5cd8469a56df3f187e0e52ee18f481ca7e3c19477017988e7863e630794989b965d7ede64004df19049fe6ef6af63f213f028fa058160aa53576a6333f4297002b7bc9cf679da4221f0b52bc1c670a60411995e66254a4e02b429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ac6a0612b6e26d47900941a48188c6645ae65cd561867f61c3c68d636bed5ab1d9a63644c482ea30b4b67d93c572bfd52a7b64632aa1e867e05ebe62513322f59698322609163da156441482e69cefb3e16a0c704a13d5c63fc928e5f53d51b60eea78a58d182f24fa8bc9b6f86eb180c7163023e6fb5dd31590ab6072da798488906ed3530b9f4744a83ad0dc1035b4c4721b47d896962550510ba156630b318d44dfd08ac0eed3d22898840427e5b0983725f20154b3e766c368843cb46205ee9fd874977316a4c0ccebe02aeb479682bb23030e523f516f2f18a40b416e543da00fe64fd0aee04013e583652f46c2fe4ca7261f343f95b6bd02143d46dcc07d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50166bf204921fa05df1c95752faaf5572c84f6376f931045c42a8dd1d35644659c9c1e424b2626513e9033a6347ccf064d10db925dfe1225dc46da3512b781d2c1fae59276d153357c7339843de3f8d0a214c0a11776e884a4ba00c3044ace336bf1fd606d092574391cefd604746fc46902e382dbe9a584eb335eb7ead8fc73748b6606656cf1d5c18458d3b7fa5f3441346f7233e975a113186b412a2b8e514fa68a62a7358b834afdf251adbfb1706457d3765f36a7934cdc0eb68e1fe0862c8087869b4a33e5c59929d0c8ae966442774e8236e588905ca40755e6c25ed0a25233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a506f1df1013abd7a466ef2b8510afefd458046c514e10005667e1c691da65d441caae8940e1bff4f2e3985db034de8381695f32b6e9c53be60ac008d638071b14be7999c3d1985025a81d0887dd79b3b57ba65e1007029f16d99d21615183b982756aeac3adaaf340144d66e4f48e87a1ad865e44d3c3829642b4d3b47754d8e3a8825da2efb3e0161edd7b672d3dcce1bd26a522004326725b9b4fc45a8144537549696377500da0e9e04d4295ff6095c6b692e3c66cbb81e475a854e9b5e1518aa309f10e701a37cda0b863b6e34da3bd0a73358d85ce512cb4c742941421a1ada2e296cfb8b9d60536e7a7e59503e3d26958a6f218bc40ab09fa20d84e1cc56ecbcd547e5707843eb90041edc2ff20f1e60cd42456eca3238d9572e942afa4af3fd880ac753fe5024b0646d24faff50cbb1dc49208ac46a7e8882179b083951fbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a04246e5c6cd9022813991d5b711a883623e5e7393db5cb7cfb0b294ce723f13303c70f48da7e4629e3754879ac98e93ebad3af21ee4db740232ac603b5213d339ec2586dec5d7a2795e69d370207cf3d5e54023bfa145e06062bcb383508ad640b81f7108e84fc39dbe82216a48fbf600248566e8f113d14add58963f2abab724c3e87682dd582239ecf2a68764fb456c4fd495402523e4141f5c32d8e2a7423e188480a80994a5c446206567c0778689161b80cccaef6547120125378449b142d57c86920d8dc6770d8864d0f206562a5d891700510d24f3c22ac6b7995766e5d43c505af8743798f07844cf846040e82d48c49fad9851ea298fb2149b6a441ecbcd547e5707843eb90041edc2ff20f1e60cd42456eca3238d9572e942afa4af3fd880ac753fe5024b0646d24faff50cbb1dc49208ac46a7e8882179b083951fbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a90f088505c733940bd25490f87a780717d12682bce986e08ff947251cbdf5831d6899d10bb1fb12266da2343ff178e5a6edb3a168a1b992d8a78671b5d140a799c60236e8e4d0206366c4f1ecde11e319133be4dcccbd8470f9c9416efc2f73c51584f44864da5511b8be27d8111cc5107e78932ec37ea1928243120513c567dfbeecf1b5374fc03905f195158bd52282731d83cd23ddc22a22dea43b1cd984497472862dbd9e919847e715a26dc441db1f09c2e67d8387beb01090ab361bb0c8c6996307e8fbc291d2cff08425eb6050f02d6240b06cc13f145963ac2cb43442905e270eb4234120141dc5fc32bca1884f0453951e1e2088b103c3023bbf1595f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e1d8533151c89015c9ed334cc4089342c0997e1d70552218598f3e7144545e7d4d63572627c405170569145606282425c688db22f89ac4538187395a5e4610162ed06d65dfded91e2f9a08790749bd07a0a5a90b56a3d8603e09d941ad5c2d580deac47d9e6d5102c26f8a57d22c2f017f8f4748a4743b05c4bc3b393496ab13582acc4f89b35601d681b5654360fd2713c8953f7f289c02b029910435d59457dd53e77072d0c552e46cfc055921d574d1c4092ac6a7f747f07fad6ee39f3b536e99202eacaaba1d0f8c4363fb1540312e0faf42781d513f70f29500ad14df0920f47231a1073d56b269a7475139c25a3f21e76c8f4926509fe6040260b7e773888b866efccada791f84996856da380b842376212cc0f65e6d936658e9b9bb78dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a500ce22f6ee28171139c0d5227b5958e763b48d20cee88a56f689830250a68846a597e8a304c1cf51f961cfe6d55e9566210ff0e43c4a64051e996f93035e8155b09fec27499e9c36efa2e8b5e85b313332026983eb58f633ba647453840a72f6b3d00b027527eb115607ed40f4d01da19b5f9ec424fffd927034a282d33f496575ec22a23f30ece6fbe83372581f470061c14383f714ba21749424a0ad186ea1e054e6856041e8914ef2e7f2efea8373dbb263f04675daa45e3d80265b7bd484ada1d1226e1113f7b9994ea42bf572355ecfa826248ce787be0f23c6a256e7e2f10aae743e243b7591bf738796bb5173aa651657161cdd747b0a4c15603531b285a22a249c29ba54223c6654b3937ff714b4bec0b97e8565cf3982a259807fd4430efe742f8a3b60988baf1426b56dd6151167a04d9c5e65b99e085776893765f16aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5b1cbb70ec556b20373c363f1bfd695e61ce66420771624794fea718f317a13990020b2f81610571393bac4e6e95d500fd984e6feb40d143e15f3b2dfc83c27da3f03d019d0b846b2f47db1e188e635afb71eb3c9040a9375f6fc66257b4056ea924f0759b5dcc0ae25a8e2699e11d2c347e170be1537018305c9a631ee676273898df2cc0a74c0038164e32ddc3a7418b960734e16bea3bd4a1465077b83f2fefab576c018eea636b00643576593667c6dbb52cb5de37336bf9110d05000a2591acc558aebe1357bac71c57b7d3c773ec5aa9524a09d2400642c414178d5554c52b90099474313f7e66b674c32dcf601b36a623466a436edb8f2a206d8bf151a2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503ccb4950c2ce2732a673023a05c1ba685dda7c6d2b109f75abec381d1f51e96bf0122829963a686d4b0aa960edaf9307aefcb019b732a63fafc085694dad281140bf0a532670c23b0478be57a25e6b52a48a3002b1d318693d2e662a7c249807d5b7565f495333693d070d79161d05176f19377c64c3de4c0951b2609ccb8d033f89412e47db390235688b778de39127f098dd74d1813241e0db4e1fef5dd03b104f1c4892d96c2cde1752051d0e871c8caf8b367b83ee746095b72367e9cc187e800e1fcc11351fc89d64760b11c768c4223030b08d5173ffd71b2fc8b1457dd4ec69727adc0659dc21814d5d027442abde8e3d1c71493a12a6ed013db2090e0d67463a8edc5557d155bf686bd0530f74dd682d7d9e5d0d3e4f1b2b21bc7b1f25530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e0d49f02edce060ee5259068b23bbe0a145b6521830fe9546da3b007a2ab84010b41253007afa94b2ec79b614621744640b986441c00645d7256d13c99f3d7373d26056cec87db7361cfc40c18ab6d1149be204d5422614511e9f555c66bfb015f39803a55767934e9cd807abc51107b0e4b33023fc72d04b1c8bb532acaed178a37e54f52b40e49bed343106a598c2c6a90da54dd525621249de26c70834c64bf7871318be210151f4093637517ce749da8ff356be0e7333ea9e746487dff6490391a0d366ad0316944f87ad527d019cbee04524166ae69f8e31639f78a10785caed3765775ae00763c3066acef456150afc03d83d97962bcfeff3710452a6ce1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a09ae79459184ea2b8fb28a6c671a1700f832ba5944172630a86758520ae0a65442e5cf14a1ea8172999050327a4e08462ba8e109af840d4dbc2db319d5eecf72a3c64e63f9651d03cdb1e43c6855f11a87847300aa59ee0136fdc85bafb5230682cb833d26d83b465170046cb89cda60a40ba101e1c2a67ab1b28e08d1e6930bb0fa8649416c4318aedbb04295835866bfb63b6b03a5fe02e587c70919976e1a494af83427a3955ca706655e71955c5b2bf5211e9dab113c32ecc46e4ebc1f7d9ca75a674de5ca4726c6690201503159daa4a22dc57fee5b17a6a8725b7e7761a3ba4f6a1f571c5efe685435eeef0b01de770451c7ef701407196c001a885744a1574e5337b0f10f1be95f1be7b50b2f1c03cd3dee2ca300a4b95c659557b3009dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50f52ca4263763994db049d801485dbc2bc94ba35a3fea0705899c1f34357dc46d157ce544b6c9e063f470003adbb0a52e8bbe29203b323a5d2480b616b6212565f6d65e1a8e9e593b55004e6f636eef74174e397d40b106063007694070c7e1598cff3f5f2860850091c5c702393d0c0037bfb91e995cb916785e821de954d845aacd914a2a32765719d2633aa39b936a23291e3b2730bd73d759ad55b7a3ed6a4dee052c4bbf656551527257c197d30a5ca64131bb67587214f5ac30fc4dd35e452af635f6cb1664237a07285874ae13e1abd15908757f4de62c3c6876ee832fbbdff93209b8df6092726237a70e645aac7c270b88da415f3db7b140b02cbb5ffa1d332f00290d42c677913bc0ffa22513c90040ce317638b9a21535e4dc82112aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a89b3a244320ac131d6760e33cbbfe0639523a8557a86d766acf4d9245845bf4b7930a7538ffc446827d3a03c30970a4a6ffa845e3a91c822064cf200aa37d40955d4d0476b11cd0b6ba0d77dbb892443c8456c207214cf68eb035c298bf5907a14e5d24e3934ce7a3c4ede24289ada7ad243f567868d90065689de04f25e4568f627d4371ba0486026defb75566c9b5278533b4c2829536e409ca2670ef91d69bab67413d4c16b3680ed32436732cf3a69c85007d0c67a6a6e90fe211f20b6499db3455e62fec958113f8e72faff63320e6dd2729effe03bf36f8332283f170f3acfea6f932d3b1fe2f8770528ba005894dff014187a3b4bb051786beaefe81429d33c6b3c6103444d1d5466490abe196908f3334b0ac01be888a73d01026b14d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50445d5b01b1e3435fae713348bf8bbc70285a165336a8bf6155bcc533b9dde913fddde82a34d3fa1783ac474b6203a9706f3bdc2f99f96e1c60d55d7ce0de4c398406e350167f175437724a4e3956d7572af3d662f210722f177a2f71d56ec9017e941f2ab509b14b1cb1c44104a52f477386645ebfec8024d0c5d30835c2787d40a0c3212276836635d8e524b9ea3322cdc3562c55408c110566585e25de7462ccfb7e5eea1d77581e2a9161f1603f42f4886c757caff573c1f798272750c6497462df04109c2d4c5bb40b125e070e7cc3d4636c57d7f7003fd7012203b8511bc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5042941279d9e54b29b5afa7261a47f71bc898805bf7ce751db2f4bb4328d3f675442d7773fccf313f42bae80d782155613cba1877c80a46138c885e7e3242b85148567a1bc329613d129dec5629e95a114c35953f72058056e9271001689827761f7d8e16745c3a395aca127280e3eb1238523e2f38a36b1df8c51b01c76a904b57f9254d83183a5d4edcbd4c32b0d700af627a232417d9433e9b2b7079937c4823e00f29deb9320dc06f2040fdc6c900baf91e12612b0e1c5cb9cb36d67247632f406670ee2f565919c36f27bef234768ad1b364da85d41e1d7beb64b4c378342905e270eb4234120141dc5fc32bca1884f0453951e1e2088b103c3023bbf1595f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503de90652e0edc56ae436f4295cd1a804d1cfa25b496237338a15b367cc00e1734d0d7e6bb299d56ee189b808de66a4477b58a8202787274dc6e9252989c90f744e4f6a4e6a1ddb5154720e42c9f76e018fe69a09bd82ce264ba1ba35552d6f3d3da6863af65f0860420ba42fcc375c0cadb4de697fcb30580c865e43c0c3b626ca59d136d18ca11c4b484816efba9d426a66b26adf76d47a33c07d4b260af20d721ed6632e586a5ce14483104fcd3e2ea7ef56463cf520761786aa063a466448ea6220126ff6057d68a71a1af9a8890bac53f23ab7a33b442f4f2b540e89bc0e28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50d0aa23157c68a453eaa0b94cc281e901ce0785573e758043c174a87c1ad98c04d0642a62f4c90434065fe13d99e50c5297ff9c2766d34c4dc3f1294ac558942cb82d960c5392b3761dff5f726779d32ecf2d675441c1db5860c954517aee060c756b3d3afa99f13d9af3c837b65af518db9015785b6e1c0d0765d744b7d0a15c64f4ed215b2f2708718b3351bfc92a277882d7735ec5fe1332f1de1b70ab190416b57839b4c7bd4afe4b7519e8ea05573116a65cdb102a4afa0a7b5e62b7fe4845843a519b62f12273d0c30a4ef7da187341c010f21fe1008e20723a3a324650446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505d4e486ef07bf07a87e75373f7116f6d189ca8607538743292f5db043dbc0276c9adcc6c97e0f43c7f7761534539f11b179c2767d89c85038534180805c35f1abe798c12a3336b12f0b1b5520ff091205f8b8642f0f3d86d67dc9e1965984b79ca611c247ee03765afb39f2c49bc83240dbc444d88104300a05aa7136df04e1c6aced511e9e6264d07d0ba30495ce3452e11ec7abd13cc3dc986a32d25d47c3671bbb26b4ede6e5ddb58f87696f6401b95584715dd302d4a64b4e670af6874056213e42bca182f3ed167b71affa4511659eff469c1cc0f62191aff6bf75bf743340a843391cc67266afa64201f4eb90d6ec1c407a8a60d4876944871bcf72137e1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3abd5961296eba822eaff199445f609736bb91a138cc16a97a6357fe641ba19b4e734d8466a612f94625f130540b862c6ae9b3f236f7698b6d66a9046de8651e35b575bf55e8874659cd770c546e193e0b64c230354130760a68de6134dfc89e113bf28448ff77d60f218baf5341ac6517d5f7907b09499f604d57647c139b981a3998dc59c01b0b111e6cd657adada81e81b0b03172b0752470f5b96f35719d270cbbc154382e320e560a931e4b3cef5653fda41bbb5fe87cb08f1f310ca6210e27b44122b97b836c0b6ea86343a0b85f7a871010f7b4387ee556755f371d90354c2b5323aa67eb1d560efa0fd1b37a5aa93fdf1297ce9f4d8d753b045ee77f0451f6b4392b1fa628cdf08f4631e7ed4ac01b690963369a43f1ea9c12ee3c2116dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5018c8e56335d803751f23ca1cde67dc00646d2c132bc8371e39126d6f35adea41fcf0bc364a1b637dea2c7100f7c59f50cfe8dd12de7c0e4b9f3b2a731df72f45ff36e169d21ffa60c770e45ea20c9710e341dd5e388dea6b4ee6db3c05053946837b2868647067152ee7985c82cbc4775e9471773179e6282e5cf95faa2616073ffaeb644f47e06a3ffe904173036b2d3bc72c55b7e4162078469a6e8b988a77aeb3d779bb91e31b95cc6e71e0768b2013d76170db5ea956ee809f3771506a0df2a8804e8104ae35f3ab431b56caca17895de927c7cd9c1fdf2a8d290c99814a9699331ccef5675078c4b96376d3e0244ca5725e7dcaa56fa72072242e4fbc25e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a8c728657f00af3c23f6621f9513471d3fe5b439053ff36dc56dc507f7c3d21fec536d609a4b5e2ca744ee00b99ec479914042690bd9742c983e85385e2e4317074568587f6b592b596e2b44d31cd9111b7d5d4d7fd7430839c5e813f672ee024dc2581805f2d03181b61e0b0a5da77093c3024441159a0dc97bc907ec053828ec85266754add34c35dd96510bd7d618582b1d57a180e125baf62a5bcacce34075eb5b780fb72f6f22b89f29416f8347ac1e4f6af764d3603aecf613a16d6c484df5d863bd60c92f2e43c40ec9508e1961773b36dfe8302bc6d29a1d8f7d81433e2d620f86d7e052cdab786b2cb80824afd2814c897e201333df954e53e2594cc81e300f234cef7c2cc4c257ba10eb28476e8b1c3f7c534c7b791c64b7f6d66bc974be19b4e7a805299c1628a9f7c762b4d70b3851e5ea6220751e5e7d33a72f70bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501b9d06487557172a9ab2ae1fcfde624720fb3044774c36025ec97b744a25796a34d8442e5b7e62198092c74cab72990e3070632a9ce71c5f5bdc9e04f9c69127af7c011220ea2a4a0dadbb432330c07e0b0661214ff44952277b883ac71ec80ec3297042e1e6ab4f68afe060be9ef23eeb041a16ccf901409cd7ff1824abf67e9790ef02c1e41969b9af150cda73a42fa092e466a1a74f24fdc5a96e32c4ec18058fb02a3acfd5210ef01645cd06727eef52112bd06dcf42824e1860dfb1fe017ead87218cf4d24413c90e4c1e31a63bd7a5375f0dd05e19b6ee9c397814af6287b4ad0bda761c738ff06f54e52e320e7bb89c44f7e6784bc1593936c8fb997be26c077d10d728320cf9c7797155a87b1ae8362162f6477d1e89ba1605ef83426966e9481f9e6f50f60cef04cec6c218c563630750c1067deecf1110f37dcd5db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ae1ea7c1c231a8b33be9353403455ba7233d85841ebfb561093502342a2133f24c9707e421fd8792be8a43867567a09192bce3a4303d73603756f022623848c5ce8d6367323ed4872f859ac2fcb9dd618295da56a5f52461abd86233217e9191dcdebaa0c161c3d264883fc115b2a0364b26b3a3e57080769cb777f78f4dd3f477c28b8244e8d9f5f05a9197379b4e162d59f44552d4a5962fa4ed6024f030f451c36c81177ad9b230143de6c49e23a304e03d831cb54927eeb22bc440c2bb21e710ebc1df330ea79aab0762ce01ac525accc5f08fa10ff03be2b74128b014f2a7d08f65787d9d6381126c207e43c5e22a2b86376d1fa096bae03f343e1ddb513b1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a0535175f7949c143e58b911af5b77e30015bb75e048e7944b25bbc27da62c2609b726e1d562fac29e57203494df95365d9f0336d7319a143ada461421f83e64a1098714d104f546da5ff0c0faf527a2db717cf067a4d9c0cb37acc4677f72f31b447b60e2571331736647538c0d6366601492e5dbbed2842df3159381f1d367cb0373e4db897c077f2e42835bec956410c38fe1c9af07401d5147e4d553aa37214e92a33cd9090454d41716c71811f5e71151e6641507f40c8ea911b4fdff948ddad784e048c9d211562f3620bd5331d3a8b495a017b1a2c4fd427504b999b719df6b12846417f2fac86c766b8dc8979c30c26017029d805040bab693af92961ec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5fa51e3b62997a3fbf18e7569b5cd4124fef1a09a6ccb57e598b31211fba121c1edda873f081655cc6c8df79de562e2265c28e27cd840c0a47d5120f8d942f0bbd004c77f2f45d76f5236a2e53c12f5b66c8b032d6f34722b898f27457f4ed34075c5c79d8259734a7d89018ab68aa1748cdd3692d63ad67db4c5b06709b6523d20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5052977f02d7489036c85ae5042ac12d5666b1030876f48125663c405a9bacf850e0015234093a4a5bd28b5b30e6388005cf160005bd65e03076456944c10f7b15fddd54481b12ef466f32390556cfef691a4fa3766e23d1627075ef317cadeb24538ead2f8c867f012e65d03243c3df2af8e0b315a5ecc37656909036bae48335aba5aa7dda79bf20281ae0004212161476a66a100edd60375582976b5705631d850994360f363a53dc3cd2051a32c1644017aa743e2800429fb0ae2cde51d04fed172e49672c7029ebcce907f48ec50ada6f4205d163b71fb2c0282475316a43b7007d5767222808c954ac70815e41758bb6f570a0b80452deb77336f94a4d342e56ac0b6fc9aa182a73036ab0beaf47d0d97f4a123cdc3a8738b86dd2e047427431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a8034392b9dc136094ba04c3c2613902d3e89214efebf8b1c3eacfe04f94dd41910eb1b643a37732731e4242144a53118c5eb572e2c5e2404250dbf1da54fcc0e37d6d978af54a872adc0e507d855652749430f49968b0f2be41e6c6f84aadc75d5e7f34288c3b773e3eed90776e77a074cfd2c5c845d562b097ce36b0594aa539962a70fd47b996d69a9876648ba7a6c44b4b43c5b608f3e48862074e4387e11fdf3804cb6670c073a108a41cca3d26306c7f01cf499b7617da98331d994d1796f4c9e0f71e40569886a755385074413ec03046dd25bcf394a851c3efc479f445e78390cf6120d77cf06361387e4eb6c46fa042f57c0db1608abbb59a9218103bd193d56181be37d5f06b87582168d7dbbba590970f9dc0097eb8c19889f3d12f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a525bb1168cdbd428b6a3034c1d157a1d7251e43a762ed037e71fd343dd82b14b128a230467482b2e6a12b16f5395722355c1b8417868ac5e28d0a3699bc6685b99d08a355b368e3ef04cd6663b7a595dfea9fc2afa3c1a4fe10b14647bcfcb5367e03a1649554466c6efce0a9b72e7493a388e54606eac0b4db09d75d3d4495d9b9a0433e0fb3a056d13f21713ae701da73b044246848075f056a66034c88c2536270278842bd008ad9dad78490af83be9318f0458c94e2c760ab13b46ec3846f0d7383a3da52f6ae646166ae828146c9d31993b0f667a074108fa15a2bfe80353f98c140d786117906f216077eafa482c44d04be32bb04ada38390205f46377115c643658d9df1267200a3fd051ab24eb92987837ea0a3dab6353707f9a094b70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50974f0260d332592e6fef9d4ba261bc102be365466a555b258cf3c425a282e748175afb247ccf2864d9f33e20f2c69d1778a4a42f66b8b353f61d985c1699386e7980f606e676046d0e147f18e2ef55081ff2a909316b5d059bd1a0192dca9b444a2df8208d75b43b5be4df5a9e32b97bf44c1e05ab75da31a7525564228f9655211fa7346c2b916a42d7073cf718070900d27722eb011419523ece57689e3c5062d2670df333222dd3a9da69067ed710246a813c90a00c04330b420bf3aaf255004f225cc787a206f8800521bf112444c32b160ab8903e3cfd1dcf486bb54b0733216865d0a5c37e4796bd317802da2d10f48f59a8cb010ea2b2781467237f0548111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504b505c7c875c80362b21cf267454fa5668c0785d7e6bdd3924267c59e5da3020914b757673914b73e54d392876b5f94967361d19bb628d5827501d713dd4fd5b1321e27d6fa6ed713e7c7b4d64641105b984652478a22349dc25385ac909540ce4d25f5d98e04c0997f65b4e96127d5f06b9ab120e895931e828d819c586cc7242e5aa65904e763993b59d6d241cac7037c23b6191216b20648c30663cb040118777276dce6b933d906aba4a8d0b74790e91f9351dfee214528768210283ff7c92a6960f39247547c07dbd616c4a8c2bd725fc1405bf0324eb859d65fb6dda596e49d17b02ae50214416427a13ad7e23ea315200cb7f9406a555ae476beeb06e1c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50c583a62a6664697866230b12d99ae40d8ab39631af319c722462e2364d5129015c900a2326568a38330e795585ebb0118edc27052535394adda8502dbb0bfd1cfe21684c2836a87b757ce31ea4ff751ba227893a745b69059e527b0bd3c2ee696915c26254a5912b9df47b4f4cbb85393ebb5e5be564355ada711d268890202a931b735aed3f7a517e6cce2ca7f7f64b9bfa8c2df68d20067832395ee3a3c173342de105f4cb356a9091a60181f85b31345f655e440e932e8cce2652e62aab6adc19484cfa408d0aa012fd17e68c0b1500932a47f8f74a0311cd5b6afa1e661d28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50637e88382e09f6523fa6fd64c4110611c435861ca32db87248e4264328c0fa627c6a622c260b84129df6fc12778920276b19a02c30918b48eeb44e19a0563545d73a7100104e9d1baabc947d69c49a3ed757ae46e9b9537e072a1272ed77d5790bbb1d20ed7308593748954073f1804fe16064210e405b0c5143e21a6d9e9b27aa410414c80537391a11e016ad13d73a4985383a7a28da6d7315fd0a3df45d699233787406c2cb2668e62c576db40e688fd280105c3a4c4b84a80b436ee9945758dba1104a7ab2581f8062751e3bfe5502850c75ab48db36b956ac09b854ca16f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aeda98b3f6a6a63477e7dd83b3b23ae2904945928f042a52dff2d822555822d4d1a64a07611470c26d630d33d4117ab2e75b08f7bbce9386e7cd2b04c23e7c858a2fb042825f0215af860a951ebe5970dd56d080705f4eb19a04b031b7d08e3481b401a5c23d75434ef61dd7d265d8731862c21519c5af077f8711f0ffbe1536014a71f2f9df5a11407c75b10d55fb4132aaab93574637347cc3d7c5569772a71b0bbe74e7354da64f903e815ce05e26f12648426d863eb30fc75d84de0cf8906112d3e4e9d022912876d6345253a1679c1f2515151952e470ad0bb4883d00d4e3bd5eb407486ad474dc257596bba07608da43f709fa11902e86d025544cea82c081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50dcd69a3025f78c79f76cce17d70c6f506f3ca01a4bf6110415828302ab2784513c471835f486002a27641d435cc5ff0cdbf1da1336e38751c33a1551d8c23430e771547b7be3cd243825dc4445db7a6c0818940cb2943b73f0eba55331081a0b12147307f012b5482eded515040688552b16c50f8f68d512264e0e7d13f01d2f71ab475cc0430f740918af45d997a57e1828b262b5f09f11837b5047848df5752c9aa057109f7d37d7f1a32dab135d5f1f41d01b3d972d526a7f364df825715413ac9045c62a811e5ff0c914280e3c5b088fbb048d5af76e6015995315a75c13510b2a4a3740363a8ee6490041d7fa08f40dfd096efee973e33d8b6ef8ca43575fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501d908d4d329ba95b6adc336cc5ef8332f5906d12e98b71142d6bb4573cef9f76dcfb025062e55673fcc7e1063d70a735ccf5b463d4e1a60391c4e42bf843cb5baf041455ffa23629e40c17489d6eb071f383835bbf54d77e7d0c514bd7f56810a298684bc6a7ce3a56849f17d4eaeb2aa5ad78245489e42e3e2a752f1a695909c512f30cc7821669241b6b007b1eb3355f0259784689a626df3e256bdb97003f14e92a33cd9090454d41716c71811f5e71151e6641507f40c8ea911b4fdff948ddad784e048c9d211562f3620bd5331d3a8b495a017b1a2c4fd427504b999b719df6b12846417f2fac86c766b8dc8979c30c26017029d805040bab693af92961ec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a38a899724af0041cb282091f8ce00702e10b0c244d48ce4c3e4b7b743c45f56194828b2eb475a52cb8c2fc27558ea414127e4037cdd69c5f41157a4998510215bce7863a72dc7614c0038b2c85631c6dd74b2e772f827333510f4b15965da16a1db5d6032735f05f96eff44b37ed11211d68d61fa5cbc02ca42ab24f0679f247d0d5550585b2ab51eeb9a66eff1eca05d7794b6c49921f4791d9db028dd76d245a22b51494116b10073f85731576a70635670638b06c971cf6956e025565d925c779e437d6cf907dca12117c57188e21bc202931be3f474c66cda87d04f816313c772e5fef3d3764846a6a4c478a3878ca6bfb1bc8da037154943542a8c1556563ed0361bcdfa906ea7a3b4ddaf17728cccd9c29f1226e4cfee6d56962ddc62321a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504cdfcb6ae7272d042e43a16086a4532c4963102214332f22c9624328c906954e6cdb330054c71508799ab130c2bd015302bbc84243c02b7256f39d736e062e157b7db77dced121766f748524732f9861de9cec32a66a2906d783d42a0e7ce639c13554635c00c4721b682e4f60bb2b5bc3b6c518d985f255606a0c1d55e381085faba94dbd875c158f86ca0b3a5a4b2a43127145285cad77eb4b4a32eed1c33e45d2786c35d48628f4c1fc4d30062a78f429485c037e064e23643b47c0b3bd28f4325022b655383e6399927168e27d5d934ca46b4be2ee3c047f521ee5197844d9d06c08f3882663863ffd7bd269a462eecad50a09770a1c36ca3e5355716e5ba2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508d23fa02e45f6316bc2dfc78fa86330fbd483b37fe12f7157150d71c72b0052dc477ee40a65c1a265a2f701972760b269b4a6526b358c2574f6f5972c169aa41caf1dd2544a28607d3af59220acce14f95c4fb00cdf3c05cc4180726394965158a2b207715f2ed4b14e08f4a094e001fb3e5280e1f44221fe514144f7db8d275354d1d7d2f3eac728826a01e641bb72e5e512f15eee0f106140c157cebefce45331237136934110568172b40599c95564b18032bf9a875694f93384b97e4d41f1536ce168780714e591d52028b05693b5efcf041ecaf3f28aa1fda5276b9f46ee2298359be7df819c8f9716f98fcee097bd56031aa261511b8b45d58b3b57420762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a47c21b1abb0a7e61dbd80e6a7ca6da1ec1f41d0a0cd3e7061c0dac210ab8766706508c2f9b9f060646bd63515c07ff7bf44bf90a59125039769ab15213350516736387280cc5c301a291d22576ee7b6b2caefd3a96d32b1bef8c6f672e058638bb1e42709adf1c471f36993d4e234d4da9c6324b6bd87d4a40d03c70db6b0f63b55a68528843854060afd54bcac3851badbbfc79042f9c46690d0b557cb15322f6b2b078da725720f3440a1a802f461e6532d72475962d4a25503f20f0f765639528150e5d293034558f0b6ea2908a634c487918d2ba9113dc31113d7c4f53499c5c9c2a6f308d0a553cab21713a6276aee8466042c543267461294e2978f720429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a099680065d8f6d2a1a72177994544503cc485a1e2e954452390e717e46be812667af1f0d49370141f08b3a441bc32a4bb6b34d39e7c0107af4a837540436dd3b21563a5149b64a13f2f113713d10a56f3a67db1357cb3948abad935f3f820c6f84da6f0c274c531a5212cf2cda84793462190d5114ccd825ac83e01fc5583b089cf9895a56e5710febb5aa0f455462481a2bdd39aab7c04f5018ee348790c06af1fa453f67092e7b2e5ec1072f24877a3b0ad369eec6b322442e0c062330c66bdda8ec7b8dc5a71e8ebd3e35e9ce2133586b6b32606c1075c079b818a30d4d651a825e72a87ddd2030962916a5599564f7565e0a5321d531b02dba61e938e949761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a560d2f196e14973b85d488120093be61246f122e4d79ad59803c62378c43d1213225604b45bebd1a916ab844a8f5e47a4c989e0edce4473cbf70cd45334b0c777095f731798c1f2d8dd5ef15d16f4b013faca04b877409052a0199114be0847ddf54f568ec31f9165fef6f726122173b8378f6070a2fbc587e588709211b223be97a1b44a3082626aa90c35e2691f165fe34e158d68b2708918e971d87c60b41494af83427a3955ca706655e71955c5b2bf5211e9dab113c32ecc46e4ebc1f7d9ca75a674de5ca4726c6690201503159daa4a22dc57fee5b17a6a8725b7e7761a3ba4f6a1f571c5efe685435eeef0b01de770451c7ef701407196c001a885744a1574e5337b0f10f1be95f1be7b50b2f1c03cd3dee2ca300a4b95c659557b3009dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5056659979a4569f419e6ad40a0e097263eb080f08a459a3280614da22c150f838833bdc51bb0b0f034ebcab2353089c3fff908411936e520e1b3dfe227d598550f0dd4059ba33284a10610341978a7e78e6e2316ba672d726f6caf637cda79012a48859338bb0a9151ab0421d222c1b423ed82d008f6ece3debb65805dfe2fa20a2fc883e645ebf5cc4954d2156add1237a89234d05599b7eae05f104ca14b422bfab330223382929588fdb075282876f906b562e92cf0f41c7449f4f786c790493befc2fc4a44736f4e91e301aef365977126c57b4084e506ef552274a91b03880c9a27c1a733655f2438a7d3b0fa138326c562f528980304ca13f45eb112b4fd2052b4440c87869f276874faae1037ab44d086b58451f6342334976500e9e70152d850c92ac5d2d4526305abc8764060d9f5049b98a192bbc5b2d76a294d03fab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a3d68d85c1056595aa77f1e783f624c0c4501674d15b7207ae450d6642124b44e3be41e56fcf6d624abe9ec58bbb507077049f47cac0f050dd8b153709fdd3a1d18b9872e9dfb734e56d4ea06d829b23bcd18a77d65bcac4312573a77f3f2404ca9520c2edb623913a077730ea5ef473dfdabf045cc82ed2c2fe0ce515a375e4ad20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503fd15f30733ccd6e1c214f5c2007ae4309cfcd7059939c67164b977e77f3b9562e58300826f2335a6eaa0528850dd657b3949a2faf2d4e186297e21bf86a96553bf96d5919e1af2a31c9eb5686d7ee2c792a3e026c61cc46120938111442c1487a3f0a67b4af7b4699b1341944f6095cc7f9c450eb39ea15965a2866279a6e523d219b4316585d5c1fc4be6472fcdf44122e8914b2efd652cd6b6f4c726e9f3e8022f61403a2f914296524584c1cd70ed8a1cf5be1631568bac7ae4ed8ce43104d2e821b072f7c4d2aa8e975f3329830b76b596551001a092fbdd05bbb43ab37446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50fdfdc2298f5b700abb0265567f9af1592ad0eb45b735a22c8d16a772ec23c9764c4db838c5ac90761f175f29ca4fff091f7bf247184d594c130c902065f95a0a402034647d4747354ee4322c21b740154f7a0262ea9b083c03980164dfe9db76a9520c2edb623913a077730ea5ef473dfdabf045cc82ed2c2fe0ce515a375e4ad20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504366144e7a778f5bd335d35797ef38131622eb794fd80b12487f923fcfa1bb5ac7d02f13fb97b873c303ab735535b3478b1f364fd5178d6bd160b40f4dbcee525d11746bcb62a37b471eb91496811d299dd6ec4da8e0775d3018a126bab9775cc40dc504e7706d634941fb09b5a3a9787bf19a0d0dcd071675f84e2eab6fe419b92abf0e00716866a8ccdd57c26fca2f0b09ab42c578134708b93b2dbefcac635f7a5b5d6bdcd4682c513933b8ae2438370a7f0dc0c4830bf8203c254e91cd2f3a3c4e025e2e39271a05d02c290acb5740abb204c617b033e7a2030d2d3398061a825e72a87ddd2030962916a5599564f7565e0a5321d531b02dba61e938e949761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa832bb1af226937c2b087452a1acbe73387aed4aaef3ee74c69a9c60759b370ed843ee3e4093f6326729ba74ed5684279b46f37772aa12780df504714898af6dbf3dab54dd5bbe19839c686d2e01c920c7c12123c362f55d2c10772e1cd2e241c7adcd2b3753725c960f5118b786c92c1bfdbf26ab5257541f4ef46094dd26696b972252233a404af73fbe6f106e3e1df324a47edcb0a409f4fe04047a94387e9d9f520c7bd08b7a101b642d740b40568afe9c0e8f25f45f11e97a4ab2bfe50077ad90087d13bc730e6f42031589735fc45bc945878557001373d300908ae32ff4665043bc0b5e1bd5e6e13042ea4b6882eec62b90a8444b2b7974661227757d25d7b53df8f30074c2e4707612449b6c6e63d80cb68ebd3498526058d4b6a52f0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a500a3d086e565ef01eed0e090b43330b12a1694b5d36a1d51f791f0720dea3f7104416f617b7d0b5545ac999603621426d7a14d552943d3b31a28bef58cd221465f854ab5761a3cd01c1b71c319063052232bea20391c10c23224a8d21a5c3b2731521f8027fd5030b434a045b72f430316cdefe3bf5dea922122a9d15c7b1fd499ed8db4012a53a699feebd6342f2bd351550d24df8de5a31bf353b59adc7c21421076b66e786e3578084a9396120864f0dc06266cca389181ecbd7082f24b209c47b6f6a5457f84c418e251fe41dd104c4192e11db444c6c4285c937fdc3476325233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b38ee016e519251c8fa9255972cd1c6786b886074e788044a2e8680d6db1a2476c04307b2e8dac78db721c15962a8f57a707e900aa1fd65d44880772b397975038c8387d929d8a57511a5c17173112073887c32c7926b42edad09e72c265da3d85c691443ea7b63f2a75f93edb63164f55803c7071d620097ead451c24674b4ab31dab1fa7793c1c594aca486d5d8b4522644c1437798f594cc71309cc37c802555a2d3919af75412377b13dad56c05427712839311e8b31d85d9c15927e67222e2f691c4bc21d4f9cfb064cd56a006f13fa3d65a29d7f05c097e353d78cda3ecc45bd736cbf2c4f62829c4fa6328f31e4dd9745afdad92685e3fd075b1768576b8576259c84fc3d8247a46fdae6673791068717a945f84d19422841358fb63cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a80cc643b6fd8c3d4defba2b9d932960329cbe38ee37622b58471c5f01a5cb722f571f4cae17e34a620c5902d0112b240fd1d52063e316228604fc280b2f011c1aed6c1333c90660da1b7c079713773737e86f167290df5bf9b1e91cda884075c2042172acfe1e6f7f6c0c230eaba7758cd3ce4b3d1521226ce4a00b61c0e00e65e43470104e781df6cc245f100f7653e9d36f4e200efb31aada07309a20852a56945c2eccfdad034e66c523ee4d9d7b7f9fc942b5bf2e14d3628c1ee1b4ba78ee845669eaa9762d3916a674c29b5f71609dea2f6ca9f5767c64e805c705d9433af4bc24c52faa360f8b710b79b2414710d861365c775629eed0cc7057dfd85efa6a10332dab380b73dfb10d96e9582b743d0c5759a1bd34d6f594513de6885f59e59220ebd09f67b6773627409c354c1be52654d9a7326252feff0d35cef93dbb169e2dbf02085cda3a3f48140214541f87cd48e8f7e839c404733531fdf44567498455004c842c6185381cbd4b9318b8bc430cd5b4571edf14135233d72a6708910e1cdd91fc03fd27e94e1f6c8e316ac63e11af9333255871e733d3819b52fb43d20c8c73fd78b064f6406178873eb1707a60ee13514953334011e9a79b02cdf4de438820574c4903aa49adccbe29c7079b3ec8406531167eea030cd64e1601adad2ccc21f735f88f4d074118e0659c9f8f295733734156bc4d3544096c155f23a37677d2c2449c53755e8490e050e8946d411d9dae3cc9fde579a8ab680ce83a0269d8b96c6cb237717715a5f259e5e9465915afec7bdba14360fa0dcd3d23d8cc564c2cef0dda143f381ceee973dff6535d40e45a4b1a6a14361757a5202d0cdf0742c392397980af166a855a7ea7a2aa26247e6f7df502133958b2b713ab113d78ea98e97dc70efd35eb5c36640be3dd71f925a60c8f82e6252bea6032232d4b054f66b4709b1a42563094e806c920ee556360af56c4706272e1e363733f95656c1fb83071e3a77f72302ebf698376987b25a7e80282c7b466c5f22840b60a9753f2d3af5de12787056be86a726d0f5e11fc50414180e4535f5a83341cac95037a3767f765c6a8d01425f9e802bc10cf24f6099f7b4035827d82d77374ec96c406745eb21d8938d03ffb62fa42002c8920fdf4652e180e34111c8a827c8d753c10d336024d11a8a1390a073e44b457600a4653d220f8e9d84a22926410570c097ba06c85069cd41a3f299c305cce103c59db80cf4e460e5d202662aa24d277c327321b7e2e75273c0671e43920a32b8e6bff6e654f7fd07322d70141168316936a567fb00ed316db7b0c2139014df6757632ec102635cef62b6c3ab213ece5aa107c7db25e3f605f12e240d53a42e8245b2c484f479e3aae7c3efc37474e82472b31c136361b41a0179e8cf360459928058f538c4f5bf3215fb997c9628a526a3c3093cf6eb5c44743e711426384e93a2800e41658756cb073cc96761dc8cfac09b1a65d2591f5a253b343627ef53ce549dda56e05ad6cef4b51ff5745a629b6705a148c2eafe6eb4cf221b4611f4b127c0dda90285c02e10fae759e32fdab5d5b8cecce0fde60453331fe3e6c3ebc434f09808c285c5c0f576324ce4270ad1a4edaccba0b881afb052215692847ef03565abccc0c0e220c0c0253c4604f3d093369804837eea6350a0efdb155f5e0f239372f413e5126d141bffac4612e39861282ddf6169e0c341352d3ef4617d51a1337dc252ff087516174b8af6e644e703e6eb0cc7259eb6660568fcb04c800626a46f88807b30bce48e7c9822f3f29124a93739d63ba587443a3770e0b4ae0016240d8b15446258c438d778044389c7b40ca5e7c52d4f2b575cf2b7e7829450e1de89895209835382018e23253b596342db91e3f508994aa224e20360122232c4f04db450aab97cb2af816977251a8f00197b80b4245d51b17136f0c2e8b33cf4959414a4d5bcd3342820318631b20f42a10c54d4815a8ba2b5e4faa1dcfab5c0b7d32253ccd3d346ef1568a574379d0302a508d198842d4406f4e7d3adfd82423e1a21173ee39d92fcc785d55dc8a9c2c2430d521665fb86889bf8b74b54efc324949a614b688035fef819d0e50519d4ba52141339c312b3afaf0a53c47291f7191d9cc5df0ce28171d68b933aacb5869f528d953248901635a2704402642460a23cced72ebc6c70fc6e0cb525bc1651cebe248746b5b993dad79d94ec3c1a5528dcc7a721f401039f0c67b220ac66656f5262e5ecba5d72b9b5982102757bc057f41ad0d2173b92f29fd9c1d73afc73bdfdf15038357b7541392be4a376fc26161136c5ddb19bd16e39f5a68fc7a5971f3b4625ee5ed3157785a1e1f75ab004262440c61e707121303efad237051ad4669a258175b079e66a1b96858fbbe3b42ef25e46b2ff285698f9acc002546775d1203f02f5665d60ab11a2d529aa4f976ca3b7663d545596b74c27f14c076573a4b9ee801eccee44d8c59b1212de59e16a354702926686b2666470d24534364156240f1612faabd67d1aad32fd0e1827cf8d3c041b8279d154f1e975e737240071c910e07e03ce857423c424a902ce5376b151b083ea3e1247b771f334211f91c9fe3fd5d81e7de21807a1113af472775991f637badc69426daf00f3bfc2074295eb7812c9d20531a5c343e5fe2807c0670e8de0003e7d04fe188f357af3d6f129bee5b0e632e654977956a64b5a41a64cf3d0809a65aac65631b6e3b55204b565b9fab15cea56f7c03a12906a5702209b9a7bf1aef469e0786d950249ba82a37c11d3f3666f4f00aed4fc736fb253300bb0a84558d350172759c7d75ec380d1eeed868124fd33f4e9d0cd500278dd73a2814f5676d7efd000bcb9c76fc9c11509195601d5aa1d623086d7b440a08d23dc1bc6d60a0b19a1e8ce5eb47210f787b6db2017254e9e24a66db554f0a78412e61c38768bbb1b73a2762045fb8cb01009a030a7b5ff09519288ac8298a677244d6ea2857b4fed854ff8a8a5f7ed76c68e39674683c330a5df0ca0303223b2c680b540a604ee5251747c4976511ceab61242bd849e060f94f56b6980135bd7352895b8c25d272fc1f5ded182dbe246a1a4d38fe74370494551e9fc9404601e00d9ac9bb1c587b0a50e6e84d760242723df9aceb55779ad663471af86816f81c38f6f1dc3e23d4082e6ad7ac4b138c7d2753b5b412a5d8c134e48f8a186f504a5a840e7258bbbeb62901f147624f040c62e5c04a63f0ecf3695e61120ef688515091b3594c29d45836557241219f437e42ef2d23763d8be36c9acfbf59e8e76f4924b5763631c49d6586e699773a5da909570d642eb1ee427a582fb43816a5914dadc63b36c64c9248c9ff7f634d28e6039c050727038a231191ad1b0dff1cc970ab39af444013303a1c700014704908774377081afe8c786f24490f2ee3156207d9244448117b4e56d200572b8f87ab132fb12c39912e12655d6b4f540bd6944120802c4581a6ca36eedfff6672249c360c66b5484967e155b459ec45076efa256cf471313a4dec47ea3aa725d83de10ba57d463810847875810d4c3fbd10e71d4ad0f67733150e6b0918101ce21fcc2a206d5f440b9a3478ce0a913c73395337d7c4636f1ecc667d679eb942e9ba20322ee9211f991dd35bc7c3c9691a8f677949a47660d226fa0a4d19534457d107160058ca15d033ae6f06da5557fdc4fa7135c3fd3b9a586631d2c7e148ee3be374bf8fd76d365ded705759ff79d1022d663f239a321bc95315f4427e6ed1b2af164e784d0fe261ef13fdd46a596105126dd018fd556cc01c17d12c161c99819d2e70b34b6d45acd03aa558c92dbf6068225ff59c7571bf106e2c1afa520ead1165b7208f4bf2b136510fce9c46fd39627a23db78746f648145dbffec720d57495bec2f5e202c4eff5af8ef980a565ecb400d736b6f29af5a61eae2b8199cebf36737feb55c9007fe147eb22c5ce2b9e812fbd033590476d9121fc4c61eebd755193d38803bc2020c105262e869acd654789ffc7207e8c8084cf004244f43d49e701b8e974e98af441353b9ba0217d4b526b3896a6dd9bdd20e587a1d13a1bcd17250c2952a5a5f620ad29ccd2cb1404957357e4d27f201764f74c0bb793b8cd81105f4234df596266b40af510cd62888257ab56773a8dd151de1e7256df2e1c60f2246785c0bc8eb3541626e699e97901032685b00c84d986ada31bc65c1d8ec5bc807df05620cfc6cefd78369af5cf85fd5fa651e7aa7452b4e460b5f2ef4df445b98dc16258c832b89cd574401208923813e304a4c78407d2a30353e4bbe9273d1ad54634406c648fa1dae6b1f484d6596ef1635153c7d2b5b8694115d3301107fb7b945ca8dc0609cc14a6bf00fef1000e68359b53fe033f485a91d7ca6113bcdcc7760ad550c351b17810224fb906e3bd7cc67b2e9be3b1b811342ea4bc1746189d705a62486710585a74e1afb052ac0818e0520d7a4404b52de1dad305314ea2b210ff305e812516cdd51c77e7d32cb292d2fc9a28473db84d325476278688820081e9f17820256540928025d420e0d89341f32ceea0b8f8f9d0c62e26772ca096d259af6170386d749013b4295262f13745758b6997aa1f03036707f210cad23e82ba4e8cf236d578b634f486a71b7f376755b3d5370f4ee930a19cf0d6d3bcd64646d8cba540f61a1741b7b566d6d93ca33414cca3d7f853f3896ee4357f4ef276b64a2fd2343441a0e40b00b7500dcf6427995432e1eeac8507d094232aa90540890f219543a69fa5236530d2e4c4ed37378bbc56e7bae025b3214e26bf199711483790a4079596d1b423b190503919179e7064631c33eb87e9d891f2b1f04ce7b00fe736d01744570b7600f56d795f73225f1cb4d1843f54f8793d878a800206c41a2d05a435de44746399d69bfe87c6366e06a67460d057dbecb431c7b61ea27100b9144c072a34d52b34e6e76e0eb0c1893c14cfdeeb7667709a15e16a70c2ddcdccf242aba5b1229c0b34f67a3d57ca5808f73ebb8603b35ce3753e49ae628cf5a535e81b8a92668d4a872cd12c703a3fc1f4975435d26c2548d5ee80cc52a1f0b74679e9f12464e82fd3fa4055e4fa2d92f53e196ea610e55b2399ca2c142fbf60f28849d3a3704a304056cb62b4ad8444c02fd139a6a463a236a07ef1a02252f6b2e4b49ea1913f2f4205fcc920c49a54711c0805d2d0f784232916f087c6871014dafc6770ef2018f288a91ba6089675873a4b60e6c86ebbd5ceada645149c5b0277333975a0133707ee41774555256cf405ff3600349ccad75cd1fe91ad46e0f0d256f066616d2112a8dcf1d5d965e7f2532c88773ff94491d34cec30d7af9aa5baa609606947b7a33ff235d48b4ce080b2b5dbc61ced40045f4fada5d4cb3357147c25f231b92886e2e82484793a1df5455b8aa050d017a66d6ee512910959f11181b8b5afda69776e5243740bd60d773649bc70ec30ebe2084fd00105e88b9075f4e7145544ca41eed2a0d7515230f048888621418f719493fd55666c379fe046f2322290c9d3a0b9a8fa74ef286c13b94a2ff4e4b21d8329ddd3402ca855e07ee834a2afb2b2577f2659f266bedf81a6c829b3ed4131f33e4e9311320d8366ebd08a8274085680c2c9004225c6b11346794c3516b4bbe67fd52890d903ae76c7f1d1f087419a57e4222f018dedadc3659363e7d1b980633cb907a75baecbb1eb1e1dc4144d4b6139ea7746b707b0d08052c722972304805a009cb6dfe29d4391d1a78130143f44363a70a1210cf872191fc94542076e0303ec1ec59db87935aa455e476ec831f62ed4778095339cc2c1e4f436e8d14172ca5ec1a5f99c40800281db86fce809f160ca34a13fa166507f5b28b1549b56f10a2bc980b912a903d0e900d747dc7e3258d5a3f399361f10405929a1da7453866ffeb24346a172f724292f216691c12533829c30a4fd2b17038fa046cbfa81818ab703c46dbb46632c8cb164b4265bb2f37c4d75d45176e3d80271e4962f5fa7e68ee8050bc443f0c86893206eb51f2722378e973f0413200c85c260d0b2a0f14ed35e92b2ce93306bdec55445fb36f0919652a5f17fb1854d1336d45e2ff0c470bbc1366b658116cc9291a79b645e82e5ab48239e3f7883b04d8b23fe4da0e07760f373c0f509031a587f93ea8876f1f6e68a2681c58d12e0dab3b222ec50012dd9547114d0b313adc7e23741df50105d918fc10b2b5036856c3bc49140022768987f37df02de214a3fa8c5382947b3968974322b5155a30aa199f622ea10d26b0ed823b6f980b62cf8aaa626c7e641c4e0bb9412546c75b7710e407153e017484746f38dba5dd481351986b58192d1660592b37c8e8fa143268cb29b4653d551dd16a3690bd35044ab48730eb6b7773897c484cf3f8ad4e2b8535780e41323e37ae82768ad8d2491d67177769862e3f247e8c01ed7e093e34370d445a25b52f483028485b126176b9ac8f5a24df000417c2237dbc64e57e36012e3437633c1aadd47c29f5cefc122a0ea2060f9bf11c9097007b15b8eb022999c8121494165bdd41fe1da8c8e77e855c4b429bab185b867ea275499d425bedd82e55f34200751736904abd4ee2012b90d74f397ce8598665c01d82e1f163c61fc97ba54b9f171df991321ab30352b258a9793113115ca822736417c4346563c5f3697c770878aa139827f9514b5e251446143585b3564f01f64b42af7f13ecd554404970254ea2ea9f4908bc9c36cf6c801a1d787e1ea654a57e0faa256a2291556b53b66644abae3e4f3291440d5479892638024e6ac5a4993066f82f2f67c34805612e5622686efc15d1bad2731a70e770856df3097381b868d66f61571a422807e384835b45d5bd4fbd9bb75d41e8cb0f7eb6fe7adb931d3a5818ba28cd547f1f1b96a758d4dfe12ea7f07f2bb8ec5b0a37bded0f50b2332f86d4f220ef28de453d904220890185733f5390285c54e042cdcca718b70c304ccb2dfa0b4efb9877fc917d39eb3c3b517dd7525cdafafe785d8c79299ba80f37280520296fd46067af08ca249f428b0381cbaa61b47f8f0c9072684f63155132f1fcdb17d2bfe75eb79b1c7ae47d644922ba895354e96e10946e922ae0d89b729ae933122bbb45778104a31d54f022509abec43b0ab2a225c2f8b26a28dfd520fcc8a405af1baa53235d3a6b619e6242c0180a75ee6e65490bb04358cbea0d1e5b3ab53208cfab0588783170230af03c1d2d505cccf8ba149216536866a1c71aedd2c209582a244b30477a70ba09992d0e2e64011fff4a6529c33e10fb15b53f1429a65978336c5e10de35402811360b88a18f4d9aa03d3912968624c91c54474a0ca643d944561abac9205e115fe76fcb670c394494cb56dd22354428a782321ded1b1f92c1d70b15d20b0cd85d327b4b6e02790bcda926ab2d6b30f008bc19fa7be264297eff1a65c18d4d753ec3565fdaf53e2f813d57eb9b331ccdede81a24fd2a313e4e515df424b456c2754a0746f18f2211042f5a0e0cb36a4978296338a0384c62c1012148fe1179c404692ad2651a24f8e7bd158c567b1031e9fa6329ecab1cd4e96826e553625c9068b82dae36933bb20d1210e8e39247ebae2c68fac130435890745bb10485105c2ef76b669a3466aceda3654352bf74c857037e7cb10572eec379173925d8367873f8424128c52104724b30f2513f2b11f9a57adb34877dddaa175107ee3f65128c7e1d122ead0d83a1f518d6548c58eee60e6ea794cc5528cf8252c0e60f649ea10f3c64085d56b56a7313bede4f54e75ec53d136b297e87c89a17de8fef1028edf962d6c17750eb23472f8362a763fa92af3025466650c016520c71f2c24350d31a106bfbfa58e1591b2a1ddae24080c803478ec0ef4bb22b2e2a67238857a0c28f631b78bb0eb0175c310ec7dc71f0717902c4decc4a0baeaf2d4585a748d20b2b61a61fa404e0de584f4891d734f514ac05f984c02a87bade3da03e886d3299dd3fd98d1c602959285dfafb3b6995f5be78bb25065aa7e3be314a47bc1324d6091cec2cbe548eaee2649db3e545320d461e16597b65d137ff4c4e45027d47601c3c07748c3616454859c6fe873f2a9ebf3cb9ebf3003002f50e248f930d78b0db200a53a04b2bdfb731772d1233dbe8ad7261fdae4de6950a725d27ed38a166552c63c192582df79a7958ec9063e053a04414fe0e761be2da4a105ec03b0ca0bf458cc84d6da1cf2d603dfea232bdac3600cf87b804c8f1fe54fec0ec09acbbed634d8e6356a3e7485c424cf8149793ea4d47e38725fd21c07910fdbf74323a2e4f0e9ffa29d42c5540d9b5f80db624185e3800e9652caf3f6bb76f7d3ae345922772b6a4031b9a33359e438d12dbf11847cd9ca81620247d4c9a565020e4adcc213f7b7809c0eadd4a5119435aa525b65a0bb0fd62c960e17efb9a2402c2d19177acb5c4439ee9f63470ae771595f5c049f1ba5a1fca18ab17e84dea52d305fd1f693bf200bdb4e90f2edaf473df55c56f5ec09c704d8bb952c5852504632b7837e81427491338852d2ee98e169648a70d7e5c9c1a9bd2270a40526458b72983360dbd2378e2945867884f524fac3dae39d3d79e5436441a6d5424cf2dd613dc1f12f6975bf323e34971bb0d33a7def93d9c928174a4ed0917a2b4f82d0e99042eb403cb41061470098192f92ebdb0bf1880cd9f3b72b5467dba27987c64bf7a6f3caab6208c0ff95b8d08aa3513a1925478f5e5019bd5287c409dcc2746529a39211ec92ce0fe243bc1d21a406f63d8468a5c9a603460bc57ffb0cb578f66057da26a300a4baffe0426269422c42227056bd8db01c629a00c6491575c64814f455d8cc829ead2fb314445ca521b4ad71311d8671cc7037665515b061fee230627f06bb072f55c97121636fa4bdc4543334783164383ecf52b99fb726b0c93cb20724cb374edc7cf2a1180863934a97e1b0be0fc7b8df7076ad0885c56a0cd8b515d9208730501fb3ebb315b00775fde1849c94378e54c21732ded0e5e7940987904ac9e1b9edc387e03e07c60413a3c00054f8d648967e051f287185455c62e1d2f0e4b1419f8811f462d03252411796fa090f96d3f15bf79c5041d20fa3dc919ad9210783adcf61eff08c6302dee90012a5b015b5fc3720abef0e1142a88675e76a02c14d6a1db603532a9163c07be779d2e221614c8a65fea45a418cd377c5899511242e7d8f973e5036569cc4be476f53e4236fa82535af6fd0c1b5dc5f0176183144e04e2b0322902335cc9165520c37f101332a77b15bf89e37c27d37007abe05b5dbaaddb789c5b3707fd26db5189777368792fdb1654dc94790e4855289043272dc63182389d6ed92944d0ee7161c17b0f7981645e9c3770267ff2825cecf9540819b5d67849d414307f4ca758df6ca125b20de8484fdbfc599e85d321a9f83563198f2a7a1dad4b324685a10ca288971fc003b56e0cc0cf35abf1e14bfd3a1364d9a32925137c5a17f757785956796b634cd73a4f13cccf6223ba4a69dee4d434e781553ac130b441b444034774f040776ce2ed3327705c13d146612ee39a8920b79e601b3dbeed16c3669b7828b9f351c6588977ccb1286db5025812baeb4c0cb1182065a4709914adad305a9d260d3b6471b23494b02e4aec79f14f08dcf933995a2866fdcd500019e6c15665faeb17eae54b6f8207a054c7abb54154516d6615f0ca5f3417774201ed044087dc8f756167786366f24750b4a376102365152d6672ba08bb6b3069750d394a5ce9563cec653f72da09aa451d7d74647ff0497c5d8f677da02c4b30e0f8453b344543040afe4f2e06cb46645e7e4651a4b4221292080d2f23e4747c741dfd0fea1933634a9c711172e152422b22be21651e25725f42781511bd9327864c481ee444b34c1b2da4136bd86a36bb75f3479614b410dd4eca5028aebc712d153f1d3b92bf06ea78493db3de1f3fc8384c35b29f641128d40a7aa71f9176e2cc730391cddb216766b72694cdd20d868db15b8d95cf1314d43c38dc6c8176bb62466db4e8e72557de5702b96c8306230bf3346a34a40289186a11f07d353f0e719f3fd858037070814e41facfbb4f54b3a219feb59e0fe0a030060d9e894246277c03a32f423d936d092562a6bc560bd6543822f7c544a35c6c78d8647b6a3858e9520b571d4aef748a1349da6c496f71c1677e453b5e238c5b3bdae56b7db277737b9c6b6f0529794725d05a77524e6dbe64f59fdb7c86c61f764654935718e3242a17dc5c085f17072e8dd07c1c75778a14d6a813734537d61e85e328631da4bf26d822d80b943b314534a54b506976ff196516bd092962cd16339c3b0a60199e311addf81f3cc5e937fcbf5b47b9ada17af8b6037b61a0ce60a2e910475b65df02fbc6b23533b63a01b9aeb335e700a56a0c7b50129b7637637858261aafb15e378a758c2bdab8c16e87a9ab4a19a89666fe00543accf89f5b95072d31f72bb46719d3372054be665d9da87e2e9b6a4b0765569463bfa3dc3600173c60f3980638cd5a8a069f2f0c71536eed02cacfdb451b8b4623bb7cac6283da9450be63c046fce2a467c12feb07d74ae2406084ed05d03aa15b329dd66d3ed8ad57732d4f5d75ca4b29736ec93e74c6a73be0377651865cfa3643c39b578666fa1f6f50404caf89fb2e6741006388a04d025476432ac51ddf3e6cf8a7191bb6746f7ddb8d1b798a1207ac55772b5b98415455539d326eb9cc7022b2fb3be8debb566adfae324eb1d26b414a4f485e0d4e6f99a9724f5486804d4fe1ec2e4cb7012c2d4f4f7792c8345bf7abcf1e11113e2c7ec0ef1f49186a2117d1b13d3f78616063d08a36e7ab2a6db77aa363fa2d9c4a4e5dd73d9b957d072fc4ec73b06db74c744d6a028b815b156cec0a3fd4f6231c07dbb55db3e88674df853e3df76ea94f2ec3e75315525a16a50d2d4c57f76300e646d53791c5ac0d92f9cc542f591064b9674a459f0094174c8ec548ec1c103e528cc86025d6b302cf3c643462ae750962877f0fca156851f64ccb357929a55b8d56d81e98e71d026dd4844349f2ca2a392d5630e89afe76f653e72587baf3614b289f281ccde10a502d6b12b2daf82d79661c70acbcfb4c47b1a21606a5491cac798b385bd93f4489ea1b3f9f04ce60a1cef60d5803bc42d44d542fb6a8132132489d16221f3c034784e36e7708864710bd4c5df630c57361c93843b73c673e905f6967fbfdb05c8942ab2dc38f4e2315dfe3598c968e654d582f183547897d9448c3335a42a6635b310d1b4a86783892a35c7e3aa09b2d85c0cb619c99066e1d4ad724ab0ebd34c24fe9211855973f45dbfb7a41f71e38f491a16fba2bf901dc280469c614b620ab8e7141a8c5313829b94a6f3811d909a4ab5a2392c8d04a1d532671926b937665e84554595c411f374cf91abbc547605be1e827f39ef3538feeae4ba2f9764a8062c555ae31a65e8ea74e41fdf96a1c8ac6332830702e11a04f392f674a26714202b26bbed5a940f6fd552ccf53fd4c6962eb08462f2f2a8bb1f5672ddb007e76bd015e5f3ef438755843741efc507e1c6094323d8eb432279ad30f23803b3026a7b105323979736c6d223f17c9f67e9a29ec0517e63d3a5fc86159bc126b74b60a335e01a444183e610b7dd969971d65a50d31ed644563d76a3f01e3c46d2c0fe18239f5d31f2482291c6c2ac6f466a093aa188cdad954d3856f7bf63d1c6a05075c652941900df791ed6d1be0791e41f0032d8d14cb2e47eec020f2b3767046492f15a1ae1a5ef9009f4943e3316aa1ac425795500e03cddac6558cfc214a7bee9344ac25445ee7686359f1486250df31930347635e152b290647dea62a14ef053e1bcebcf664562d655f3adaec57197590743206bd2823cddd1dcff605424a21472f9b3cf716bb4b6e63e309e95e2e8ef970b48b1e122f062f315a782d7d51dd9001805298614952383deb8b472a7e06d46d717519446562b76548c5f032e80fc75b20eaf65ca927b93fb9850a494307f073e558ea2143d9911db19bca21596a2079619e6946dca78e1534cc18045f854816e34c9632c1e8697a49b5e6253ad5ca079845181b74e60754895c835a9cc47c0d122bf5576a683f1e837d2449496c5a2146e61950d162140bf2baa847f5dad911e2e80a737499a401854a347d59d54e53d216f335e8dffe6a4136b03a7da0e40b5466ed742d47fb658bc99a5e6e570050c7427b296aa69a616b5dc013da355d75175d317a4c4b253759e58258190e032221324e0b75eea729aca83656cf8a34662391e55fafd5380256b4a564f9fcc06d60b5410cd2156a07f4c5114c4dc4b2754551a1263f9f6f13d154a17023b4685e98fdc84c604ca21f12a3870b402d235d79df3238a79eb92c0dcd5d32c19df121846c8778a95ae2094648f76a6ba650561ee454318808fc048886cc6289599240bd0fb7180b91fb09ff9d5d3e4979e333cab6171ac0d47b3f83bce933bf77e65862065004108d8f3f67534a0e5b232f26c439e47b602eb72fd734a22451fdae66b88431203cca887a04893e12d1b85d176218e86a0455137aadfdf0403826154ca92e890c157f2e66ccc442554b93637dabe67d1385c93651f3b7507196139818f03639730620f809d2f7844030963305b9d1e9777d792d630a9464738664d926f8379754462d8a00e501d17b7f41b36bb09c007904a5975d19b7ac06fc5f863b1e7fe67a231103583add2320d6c7373a869173039760557d43661a5c84a17d462ff6624982d6ad552054c91ec2f5da5010f68c24cc8822405725100907d82d302c0156798d3b0d62f31f5621c60a1b7a00932b0629a4154b047bf750e63f691a3519e50b246a3b2254d05b6e78e2a5479104b0633ed00b1cbae04b7e3e0309318b229106fd432665d26e2406c5ca8928e486d259abc89b0abb32580d4f4097782bdfa61f78fc7764e0dae87e17434700564fd439764b6929b28789473fea122b717b3f12c46e7a5932e819026234d47e16b10508cb1cc76511becf540f047d5ca1d9d657d39e1a78c2fdec2d172b71028308b43c3f17cc3ebef1540cb390471b8742590d866a93450c92ab48026dcd312dbe646d3b87f6028cbb5211cea88962bedf4d53c367316225bfc3247284f8367a4d434d7fef58779d467d1dc51fe14e867d4d4e8dea9045cc61227ac597794ed6ab2522445e7c730bb2f316b58d8471fb0b0e31de9aed044a0f367afe30b90c0e28bb170ecdac31faead3474635d40e2d10f37e7dd18543c72e997a910019454505ce79c64b0c03d9f86451b4dcbd02f2090e7bb135ba28e1bb796fee5d5f72b193313840479e4fde39406d257a0a48c86e092916c37235c917e07abb7979046a39533f4a0a651d13b36f3c6a60d3549fb5d9089195020bbf0a5f7ca9fc6b4e7ad2ff694ccaeb7de732d92bfbb7f6330afa7b0b9acece1a6a9a6c7081d45b75e63e4464d87e7b66ffd71b05f403cc3a4a30c82d1ba1ea28ffb5223d9329e564c018e465a4fc481e52be830eb5acc9427ffa5f194d0042197ed1a96080824d1efc88c33ee8d8ac617f11835d64b9722d76411521d60b4f273756cf04ffadf02ced110346aa8bf24846970b1fff31b6435082fa436847500f25d86c6dcddca82170d81059bd64a00839ac19626173b733fb99e7552385066aafb20f227d290571fae84931b6c59272a50f5b0d5eaf5b11afdef17442d54240d3b98c0edfa8396b6b096b6bd44b324dd3d4e27b324c110fed5cad0759d43133cc69385dd2dece444a696c1f1440525ecad9e9696c32617604ceec4a2e81f439f144d851ad91f42b01badc2124488b34eed59b611e206413f9b46a34de5f113638a31911ec229d1d49f13903daae804264760265f1458f15eba74052b9063823a449732869f0b04099bf78073af30358927c8f22e2c297527c33a9413af61020803223187ed2e866b92c436140be656eff93621fc0db8338a81d955aa305234ab8c98d2824bb4c583356520936922300876e990b0c2db46c53724d2de5789f6c80aa1c394d6bcd728d9aa62bceb8d028743bf7727eeb5059dced445f2ea37c0bfec6e24ae99f057e8ab7c321a12a0d5397b54412853925058f2a7439c4dd8b02400fd423edd0d80eb79dce292049887c3857ee6101e4c14ee61d01092550af769725fa1734615d6cd1e0d94f6bcc2362acffe667943f8f0e601f8620ef4f1b1a1e33d41bc271786985ddd47494bd7903536f6301dced5865737e5c62921f602d98e8754bfddb253529cc1001d8e00349644816493d3d3048cbf3424a367ac96fc501d40d6d759e10404f9d09c64852336bc11668af7608302b6a991572852e7934839019e089bf2112989f6bf75a5c6c7d9e28036c083d6236832246ab97ad4b3970cb1e46fd71528dc8074681f9c655fb5c7e0af2a6c4225ec16d36789c9f00a5e89372188b804416a5a146971643052d80d5669523de3a3350b069ce87ef1a7f62b2381ec2d97e22add8472401214717d1941a7ae72a129311f44d86434d5cd420fa432290b1728cc9e828f4a2684f7ad7d35ab5b0bd3d73428e686a5d996263a81423f7b7326c96a1f60ea188b01dd5cba424dd228f277ee81c5e1fc3fd11b4897d0fb9110d7c1ee54b5a7f450f18e577bc341c90bd10a28fbc1c34370e300804df563a463879a547a2709390bb1e8da2281f2a87bb66f0d5ae545ac00918cadef3469e45107beb6e745806dbdc30e3525d76054e7e47e5329845d255326aac1bd0445341da5b5ab57e34670a9e5557654352530e8f298267ba79cc19c422579e577476b4b3596e3e3e2b7c910d2cf6dd9246f7fff049affa516e1607aa1126403d627a6b387dd1bced1df1fabe1b2a500c7785b5e02044e5a42c6787c0649dd54f46e629bd1808ee1b79b71dbf364a0e0554e7470e2a7982fe145daa3763bd5e8203f8161364a6c7fe317f600367b082967808a0d268b51ac321339d2e4a5158155429afa8130fadb43727c65a0fc21add26d68c666ec2c7b52a47d85122d6527f0b1d39810669002a000db89503275e9320aa943f14e148d57cfe8e493ff2be531f5c5e420aed11043116e8536b4c22546746d9b63c0961d946f5b4db3c424e2e520f4aa55264b43c0eacbfd36fd066e80157671143f815cb2dba3ef9100ff4a14cca433f21b20408310283a2210da63d13549e9c4207720a366479e50f26eae13361af64151234af0e93e3cd60df2cbe4a472acf070e5edf0a6cffd96d35cc4b328d574b0af81cb777c24d2a6a8271777ed6a4be2d377b7e1d2602980e76077c3005cdfa410c78e4273ef0f909b1ec470e2b85ab0f8842652885dc576ecb3483614b7fb276a96e210c39e3e442099d135ad45559784a347e5cf143976c05b81916e205421a44b18d530318c608abb6ee7d9f3e96238a0b7a1df8ab0a02922e7c13f475cc35980a84466faa247e0abb141e4682b63ce505f82bf68d7365de360f2795169622d241e623ba77e43c1c480b518b82a15c7d7dfb696e66eb209068443a26b806292c3865283673716b5b8a45192a603418ee5b1259e214782fc916836aed9ccd52f56b990e2724965fdb86f221c378ca616e47684637143c4bbeeebb52a5e0e30a91bb3d6bbf98182591b22e57d0375d3003213c0cf4ce0d73a95ea95ca69007163805d66dbc9766203825367cd1f402702c7ba23bc396eb1911d14f70453ba10ced5b3b6ab8cd56539b0f166bd1d6622504663769dc6f12743b86c34bccd9185f8f7a2867615f6072bb465b3228bbae0e1739c518cfffa7622d9e277c8fde4a1fb28693724ff87d4a63fe5c0db4e4df4e6a682d2a8debd747a9538905db34615e90b4e90104b46c0023303d6381a12053e5e68f34931efb5202b9f96546e2707ee2590932e317d702fad3c263a6be8d5665ee754ad38c3b279870b7607be54375538c0276012af7606f13865a01a36a7c0daaeb684986f61fa5219c2f22733f472e59dc79cfe99e193e158937957b7743fe82fe4af9dbcf1e32033a226e29a10af3ad3c352f9fe905211e865cc933c0279503f8789b0b5b7d94328913609cdd6d66561325c9ae137186cb3f6373215e03cebcaa730ba2e86b23f4ad2e1907b9146127bf32e553857dde2e0d6d073a96624eaeab273d55c1690438c56061f97516f54cf63c028ccc03ded5f3021a484e0c6d44482c452b0004adb48909d4893a41102bec4b08cf847e394c2572dc2e58160dbf29603a8b3003e14e347c9e212f0fde5f2d15ae2d94256ba5ad1e906851747eb889620d39d41675b65608dfe0ff0245e37325ee54df7d1a41c20e566b676021433f6dd090b056636b11568870e92d6d335f4f012ef4467023b955bd59220bd8538c5b1fdcae090105df033e7b1d3bb4a664630e4b9d67ad283320e6a85a6ae21c1b640590a249111d2129d42a6a5245e3752bfddcbd559ce2785c69ea6670a04bf8393d85c47db267f508c3f2c7227894c46f48fac75029f4d923aa07f73677b5f51b0d49a15897a6f148d3133b1ebfad583083ef233e0cb8121f01a19933e491c9426d84125a9e0a29617ad1f5031579201aadd1792135dba45a98e74d124f73002cbb5c0d1b14f5062834619704b3bded18a767b53e5953ee4cec6d373eec22144ce0b0354345f3c06a716aeb6791e2180cff3d6a45d265e452b60694776e31e62b6e88cd1ab5363e5ac10e1534145b3238606e20511c17af49e6b20024a1742423ad21d57cd9f06172126ec06ffa2bcb71b809c84a2a756e656675fc32ffe48160b8b9d05469f72562b485e44a082f81085ed52911d0249e7dbfa6ac63d64d94455bee88324a1333297e63ea3b55026c19a8392d3a1641f13389281d7870f45b735228232fe595d43320342a0373ced6193eb6da5c65672c16bffa0436262c491bc03775795337915d32cb452b870ddb767a244807226f9023339a9a462bf32302da1dda71c632791c158ce12f83774a3d6d896e77016ce70ac8d0e761ff9f05039376bc7eed43a66661b922066905e911cf2dfa1b213e25506f77ec34bcbee63904a2960a2895b270cc34be128f00ec030938a53eec231a0802fc7c08a06bdd1eb9964b7e970a9407f6acb4402a79a55fff5e1245c716c11318f3ba049a35f122c089a125d07bd44d4f31ae4f02c69a14f95b7c20bc2164794bd5d5387326f71c378f264830b27e260fc12c4de84f566740023659494ba004cc51d974dc8c142d32a88313bed7fc0e7f246849a77fca508dacd575568f7f0446880c5deb2c561e145d2d42da130f79fe7e8a6b6324f4425619c046d593a36ec5c5f83be56dd80f9ea6e540e0b6694b6f273208a2da201bcda5e93fc68ab45b2e8f3947282780651670836ddb81f700f6e73a1fab6e534c7c13912217e2716e7f8a9f5666144940a56b8148e2a5be5c3dee42712cac1933afedd112d9634123e4e794436d20fb52e2f078795058536954b2493c43f4db37222e0912788f4e26bfa20644941c9720e7716228c6323506cea2f803f2d7113cd905572627e2c00ec5178e4481ee3a7350191057a5ecea62ec437948bbfa8212dcd45d195f868608a161cd28fc20b446fe0c5b2986dd100de8132138e9b91c505b65fa3654a6a655fed7c93c7d7213759e0c59301e7991645c603d69ccf52a501ec7cb654b23cd59d49a584c0d833b7eb969d524f45668517e6dde5c4ff117004954723de64d1c343ee62c6e50bc5713b3c7a7302492fc6ddca7ca0657f7ce57dcee3c7328cb8c4186bd276f25ee541ad4aa297481b6ad3cf7a0e3158ba0662de8043954dda2ec4fd94f8b4ebcee6419e5da5d75b5d9dc5faf21ab08327e81149fd09261238fb30fc15bf5229df7c274c0a62d393e7c311d709de816fd8eba50e2ec401594736a57fd170f362b7ba65a0cff8269eae67b4522f1c0467d1edc2b6abd7f7e4c9fdd393f8fe51bb0c59560d5d8173f07d40423c41efe549d44473a592eb853990ae6447d66de6a8589324289dbde430730be4c14c92a039a33717de6a2b901fa932e33a8d1460c2081234a7038c22bf0ca260bd5f2b501ce442a77d25d2626c0b9b95b8848471e0d9c165a9ae0ce4b36aaa572b63a6b1e7658f238d2a906447fbde114e3c08a753e46e016207e3406936adf7b59b5b4312d783f6bd4d6044eedf3be6e867c700de16bde6a2924945e4b7ee3021c674256fed2f90c79085e11cad79422667fd06c2ba5d80ee1941650ce76de2a35dba1532d016f4767e25c0ddbb1cb13d363101b0197454465f31915172eea207beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711174337b855d79a1f7a14c3882f200a29244ff62c12e22b207beadbd01fd979fd709b4889277e4ed90329df2e33222dd64a310c932854071d22ea8a757ea89ee103b19ddd14a0e6122e65156553d903e4152a245b6ced2a6f45e1233209a799c31cafd2656a34c443193d8c4b33bef0bb6d34a8844bdfe8971021b2370a41a84461a7dd1b4724eea50a2495ed0bc3096d532d05ba25b5d86e54728c316d8d43751e6e7fdb7e07a974208f3c0c58c5c2796be7b60b47f4b36a4fdbfea70fd0948d52256a5c6da780f6258f8b6e2f52c15e41ac2d4c1ef7b8983bf6afc90d68b9d20a80776a5d6807d37756483b794f94f433390e143354a2a24b286512454ebf7a645633b05dce153c590dbff850d66fb34c56d4ef74ca79536ed6ac272030ffcf74429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041954839a55a2cee97122126a7b25c84d48d477dc4f9f579e4d68b1c92be71a5543eb05ae23e5227e6d04ef9a5376750301a2670b6aacae6952b4fa982770f9841607bba60a931bc326d25c3c6703d2112b4faa250d07c7d96c1bdb0a549cd2fc21933eb8499f6aa04b4a50136a39bf9947c841d2737f37af4df21de11615ffa5158c1c3e6d8fc8a21fd09b2a6c24c4680171a8002ad538c87c168dca6e7eade43b4e8e9f795dfc42615362e220a3e0845aac8ebc620563012709e40a32387e7f4f37f1fd17122be81b97b82a0e2a602b342528fc51943d6c0b688ad00742265c2050a25135dadc325b2d004407c0e1325bcba1a559edad09345db31472cdb868565d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419e789724014f3326ab548f07a4754411c9d3c9a0f294b4e13aee8fa07aa6c317eb049f25b8ea8975ccddba9181da5732748dffe684b91ab1c158509092fc9ed208347dc78f3a9677a51e4cd78b0c6185397d1c25cde9ac366efb4df1507b365215e88440aab17773b92558d3826e0c56d7656f0250750ed0c21a099564aff502f0c68206bc44c2e3a21621f79857fff5605f2b36ee7de712a51c8ea2beaaa3c56ce634024eb204f1637bc7c221a980811ef883325aa66845d5a33840a81da8f16cabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711174e6c077a13f3de3beb27dd2fe7a7ee00810a004367d0ea0010613f1446f45633f50e404cb4592b05e1902919428e8474ba3d8159909f395d50d38c2b63cd203222b58c69b9d04d6f91ac8d0605c81003d41b465a741d88221cf445262a3d510ea07e0e4248194c5ec8fa840e45f6e72f26546e5ce8e0062bded8be559486f56db1fa282d174a6f7ab536ef3c69fd2e16274f7657a60b7762815d7a17a88ba41037adea666eeed5748c12e678f26f0e56021ebb59f5683c1cb46f9e71e52ead2f3543de0458af8c5e3fe42578355135372f15a400130c475aa2bb463d7a7cb119119deb1186fcb211d575435ada31e85e0488045c77685e453410374003f98e05fc4b0a2047c6b70ef09bb17b07dfb56bece20a4ec32a254a3cdfbe048b72772dc024b232af2c7575d3aa7c49de7a6d555a41824875a6337d68233a407f63ca62bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711177f5562646b8dee6c42e0db4dfc6dcf35ba6c486790a8fe4d273a0337b5843c61d03a4e0fcc4d3a5e85353c45f9639b34670cee17709b3a41d7139d034459b619d06e9a259a2f0f75cf42f023b342ca1d937a8901f29e9a398c4d8e7b1a95302a4d264a711032327ec5d697102f47942b2d122e1244e4465be24a50016c523205d37d5e47e6ff7f7118bf332045c70a335bb3563163d3fe1bf6f33323a2601838b8ad442d7bf7336c28d8577c77fe862791a7097706b16b368cd6fe43f09659045fb357419f5b642d58717c75f118bb77dc2b007506e40c6d0c6fbc7853ecf958c0ce16613c803d563dde043b60ab6b29ecfc6763ee67fe060fef294d48f0272c7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117586cab3600ecd2750528c86a6afe8e188f86834dc4a2b07975bde0458ec70b1ba1a44b5f8fd45853f16fb13640b10f5bcc90d21fe7e5f65d07ccdb3b12a6b322052e7d25f327206556a702046c064353bd65ba27f3e6361a374f913b2f060321af3fba7e14d4ba1afce5107eaaa44053a08bd80ccf1e345c3185117bea3c607ed20016177dfe2d5ba9639c0f90ca4e3305be2f02eb3e79001a2e772f46ee5d646fcc3461aaadef519a1978692a5aee7255e6d11c718743043f05d26ba7959d02c18af278931f75188c010a0dbae2f251648dc73e519db3299053602ccab7d94470538372fbe6334de29c462e8d3b71464df4394533137e6b9649af7020faee7bde85dd4cc877f35ecb858d7475385e3a592f20585d4c681980dc9b253eee4b4dc024b232af2c7575d3aa7c49de7a6d555a41824875a6337d68233a407f63ca62bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117b110f46981e10362be040746d526a73b2389362979ec9070a975b844c450267528795e156a70006e571c8215ce8bcf63c45cb60da396bf0e16eb7369e2b12b439bc3c54341cb7672a478f001d414f312008367740a95dc1841ca8c597b80b5363f045c3f8e51371b0c6d8d1fb0bdb57371ec0d2a94f08a1faa1ea4736e670e1602aaad2787d5384597a9b16cf3f82b6e6181342607d0aa6675fe4d27eb0bbc489c465419dda635423e645e5d9d94fb219afe4e0e4f9418160f73f9248fad8a3bc807562083477b1f0ba6670601498765e34a2d22bd5fc45f968a302c662fde799b10aa1369c50e5251c54d53a2f018092ace8253c541a33e610b4a504d2b9e004b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117f440d478f55c672a00a6d83f3cc03416080a0d0a9b50ea2de0d71a3541988e7cdad85b733c6b297259374a66d917e71cd68c702761aaec42e476844a5042d32eb38a5e02f3d38f469a53051d29d0b33391e1d6402611d3590190bd1ead75ae7914719c2448f4f870767ed658cfcc79034520e44c465fa40a937ac9383b2465439f57dc59a4fbcb0ec29bdd5fbcba1b256e8daa267288493e96ecd15c252323048cc7465f87af5705e6176f226b00221870510759190b3760f6099a371ef10c46fc30c436dbcb28497349c5190c733a71afaf436f24c3a2033a631d5ed1317660324d9f4d9862f309223c337ecc184c4a4d4f2941d911857c0841a86550c77f398a748f19c8f4176768feb2372d24da00bdfc050f55a98b6e324d5a593831381523bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117aeae42484872937e0702962ce7ebd152419c657cdf83350575c55c74b3a9c32bec0d2e68df50216adcc0cd0699bb2430889c952fe2943542527a632538b92b3d7844cd3a68e8d506dbe3a50ad74e7e4b05dafd6276fd16484a8f1f43dd398f5437aa3e56de86550aeee6a84319bd237cce32b16476efd665c297d2066934b34e2d148a76ff00306555b00c6c0b093e20fdeadd4be331be5ff4f5d410fc294014a244ee28df88a956d3a677519fa42a2a21e0716961d2646ea325f966cf25a6109402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419513ce434be64ec39a680cf1a118d757cce2080663bcbf7526d4041361017ee6d8e948e1441b381778f5e040c48f4bb6c0f61641760e5e03539c7cb5e44998b3eb7ea3f0efd61fe077a87730aab20a72b55611d74d72917624a81d25b1d796372e5d3450fafc4a051c68adc013075db0b9d7a2323f4b4414e26b9704afb88e479ceb0db6fac7733179618c51cd11d7478e5fae82fa0a2203dc8227b00ea505a208cc7465f87af5705e6176f226b00221870510759190b3760f6099a371ef10c46fc30c436dbcb28497349c5190c733a71afaf436f24c3a2033a631d5ed1317660324d9f4d9862f309223c337ecc184c4a4d4f2941d911857c0841a86550c77f398a748f19c8f4176768feb2372d24da00bdfc050f55a98b6e324d5a593831381523bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111743aa190e9461b7592e6d55030768d17d572bd54e4f2a0b300ce4133e57ab172f728b4e6bede4086d9b7fd642891f6c0b725dfd30622d2d3beb83a87ea3c1d6033ad3c26dfc26877d1bee9a7493ed064fc549346c86dedd6bf1a8e9555b43cf787c17fd4dc9b97231e7b17d4809c8ef2149b05c1bc7bb5e47158e1b79716f984e8f928711fb8c7c18008c6d01f60988771e9e1c375695b16ed139e171accee756cdb59922e4c10a46e15fd75e9319b0799c47366a3c8cbb0463f1390e83856e3167ea8a568808d4677d622f3f6040d45bbe631450c88374007bd24e48b6d5547e9b10aa1369c50e5251c54d53a2f018092ace8253c541a33e610b4a504d2b9e004b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117d08cda0bd6dc907a09fea5163f2b4b55e234e132f244dd08bec5da7950d66726ac1ba5505334544f0c92172df320c62dfdcf1457f6528770eb77f523ec5cad43f5b5d00f4edf1053f5426d3e01b9b80b67d63b66d681335b6c886f0c40c60d159e5f6b17148b491af15e085dcb1de4775003b4553f6f8002a4d31c0348e48c383eeffa2d6999380860c46f10a864a760b6d38208d4dd384f99f7070f3729e21a5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419ba83e429a3dc3b773993d60d28aab47a1347533309ed8f5acbbab7584d835d069d5ae55ecb49a07b8db4e41abe2d9a047b6aab7c175acc0c6a510506f15b44162837376cf80f3029da49440b1583d2429db8020c0904d2110aa7ca441c72d464b9b1be110117692e6ca089712f59140686e6ba331875175880cd3920350d68385de9f620403c9e725323444b7cdb5236f7972022b9271102a20ae3129a49a808ce634024eb204f1637bc7c221a980811ef883325aa66845d5a33840a81da8f16cabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711176e9d16544e050b5c6259341ab729b8384e26af5b34248951c7564421e789c176bed957062a1573232b375b6d72e56510fa4e5b7c6c3e59392e2ee30d9644350e1f9f2a4df01a046ebcead246d571b82a7cbdbe2b96073a53fb409d32390e36413116f65d117b1706f9e0344f3b2f35127feb6106e213191549e6524e4407b311897db306ff924847db83c31f3d10de15f763a055c908a35fb62c3a7b79b4cf52c0505b1968886b72f428f00d7bc40635982f71051454a14e08039e5c59417f510c268e3b939d111529c91e3e8dd2e93ad48af72a79ae5340b38d153e8b4fb250d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711171a7ec44f9cdeca3d388a9c7094e1fe1d925f791b1719957c43599b3a3911b74311477327a1741e6ab3d21257afda1c231d30f21fab3e084976f8b46ba4c2fc1c1f9f2a4df01a046ebcead246d571b82a7cbdbe2b96073a53fb409d32390e36413116f65d117b1706f9e0344f3b2f35127feb6106e213191549e6524e4407b311897db306ff924847db83c31f3d10de15f763a055c908a35fb62c3a7b79b4cf52c0505b1968886b72f428f00d7bc40635982f71051454a14e08039e5c59417f510c268e3b939d111529c91e3e8dd2e93ad48af72a79ae5340b38d153e8b4fb250d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111778074a1adcadf262f54f7900bd44a70f67096a78aea71b2f9b36ca0bc642476262880074dcd24f5855006111762872017f4ad8186227102fb0c1c421f43a9b1df14d78193b05f067fba17649ab9f7f102799e0095d2ff5477b795914ff082d02e41ad40686fe9826844e3e7da789ff1d5fbdd506030f063d0b8daa116c34cd65fcac4f74ef00135eabc610093d5dcc17e85e8a5a8149ff54de952e54ff5ab828bc66981938e2d020d6576530918e3770e62e956c5b371c5f65f2fa3540ac500d4920df18abfe0d287d36ed6f439cd76e602df61fe4948c17a920200b5733a30ec5bb03589d622313c3cbb233c9d050533b59145bd15fe071eae8203efcec06611739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041948b4b160f361687a9bd1832e34d7496f1607d84aa8e7227ea850842fe91067079ff6560bac7c2d3557669731bef5e955782a075ee955710393752d5e26a6667b6665210f1edea4233afff577a0cabf508f2acb561e0a726f55c92e76284b4f3a274043021c04021511f661114a303d6810f53b41294b780b7adcf0294a2fbb525f7c823deaefd72f3df98954b4dd69744216a0776a1c78179adcd24df77edf312efd07409611125f0be7920131c169461fc2f132bc155e2d1080e42909f14409497f0a02ae3700063a95ce0d1925ff082b981c3f8de656222cb4135d0f344240c5bb03589d622313c3cbb233c9d050533b59145bd15fe071eae8203efcec06611739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041921259c479d3dc00da4ee9574f634c71f0e87fd30457f2d0b2d72b50d9f79e67bb2178e42b798d313e279d931daf8b926ab5809031f368b21ce1d783abeba6e22f76e9b6a32c6b77dc0b8874216fb7e196d2a516b5267bc50c1a1e1491e4d44381f878454832ce06d98d55e050dbbc652de398665ba4822490cdfe457c999d60a5318142244e94841556e4b6f93b5207684067d6146e4974854cfbf27467f345f5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f3a63c4be8bef3322f18142296f50a480b198e41b7f7907742e3a84cf9b2ed5ccd89f2730a3acf11a955f81f246a610790fd0c620c219d067858ca1a86ca3c08831f7b3fdbf51a684f50837b2ab51b3f2a5cca1c1727255e2e137a36c6e0ad1999a172033079cc640c21030485f1221263b54b5113cea2045697b27be320b62557fafa78dd6c2022a817a0083592c40233c24f2773f4604e35785b0c932cd95fe69c4a0b015b8d7a2b898667d98c2926cb41602f32f966422d8e671050efa76dcabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117f717ef1769f31b4b9db54d6780e71d033325897c1ec4342685af175c7464a961a9c6342c6e506f2b7f038210a501503a6bef924d77239d0fe63b337b7d0e3e0157311a229e38f0155341a10284efcb13976ed4205f26be710deba12bfd612765c79bc453c4a4fa6f14ed4f68fdc4422c163c79751c64cd577abc82112294f61ceb81f00a85d3396af7d0d07155f07d316999f47b5cb5f15449b4f02064a7d7172d037d66c2263531805f9674d8253376aa423c2b39f88660a05c440bc249bc58eec1131a854d1e705fddd51c7954a40b7545cc46ab2b6f1ab2936525bd6d651b108a82641d497450f0d83f2c9d296f17c5b1e47936aa0b50eef66a4123a9e4606d9e352eee483364f107e270ef7f660ec8593338cf7379150ac8b97181f41f73a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711178b290747d00e4a6bd1c6bd5f4ef4287bf4ab560795e9475e3a78897214f99753a3a6ef234ce23f7e730cab4e1038720bf2b9761b63dbf215c44a84107cfb9b57555a436ec78b666dcd993e7a541802743fa909741fae0479834a644f4d95f73f77df17673aaa943d3b75db1275458622e850cf743e42ee18638b4d58e0ac7b16742869292d7fb003ad46f01a84018279ed1cd32ee63b4f5f756d2271b347711a5593f328009b9f40b51afe678b11fd59de52c771b1b52430e8bc5b56fcc6523c9502a33b5a1bd2209a8b3f7861f30c15bc918528cdbdb60ddf062660b83dc077377905542c962279d68ff736b11eb20da2463b206ee03051759cd2702b101e642193ae3670f74512d90ced603cf97a054286ac0d4fb4191fdb32ff6c4983087e60c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419051ef913c6824e3934e3046e94e4ae143b5b1c667ea19928d3c9e47d58ae34684e8ff41da1fac757e65f0a6f2874791908bb3e7198545c62ac472d3abafe9e3e2e07f773e4f0464df88fca067bd11b2f0588b9000a0865137217c9354e2c6d3ecba4a70b87adb609d4066056dbda0148232de8681d12dd741a5d3f0efc25907ef86e5971b8cebe23a7295b7256c58307c9144b7b71dcac23e9853f4f1880e752d28fdf372534635d6b757241910e30083d71a31e126e79340247967290951f70a0336225eda4033b23fe1807db32c013e713bd758b227a25d181ed46e51af1788a289c4912b50b5e1cf6cb06205ce2644f1e815375896a0021c7417bd7b344425d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419512c834b0e35207ccb726402b371d34ace570555b6ad5a6a83a7e86147ce8a54cf23fe3e0f35a713dfe050788919d84860aaa871f5702526f2796d11ec5267173869b320b0a84263edeb12595dfdfa774b83cd6a79bc875e2d79cf15fe83db1a039cf33f6fd79f5c55605502259dbe49cff619764928ba2474ff4c16afbd5f1dcd895738d84d1241a159af237a12090546fa53684eb6f30074fd6735a034526296ecd53da3a5982d7ccd5319da5e5d19f140730df97316280d9e9d759cb5fa4f462d802b3b396a3f3ac9313c4237ff7a41d0571e3441aa5956295167683ba848108a82641d497450f0d83f2c9d296f17c5b1e47936aa0b50eef66a4123a9e4606d9e352eee483364f107e270ef7f660ec8593338cf7379150ac8b97181f41f73a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111745d56168f54fc72da1c696290c6d8e381aaede60f97ea812d6899553f705676fd7c026246ca3914def20d966d3b72d1b35cf0844fa0dc472db858350876ab33dfd4679161cbdda5aa05b4d2f0be900743a41e06a132c2e3775a36e73b2418f6e2dfd6c5d946b5a56921f80357c076140b9c95e1416a45b0557702f306296dc7ec7f1fa217f32cc25205e1f04389a3308472c004c107513711202247e6e8a6337a82cef5c9fd1701516da482e479ac534d8f5ce499eff310f9aaa935b8d8ece189402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604197f26b23abe27a0775ec4e069bed7e81aac863c7ec2157e50edaba1090e3278086446b024aabfb27b31555020e3dfbd503534536e46f18c36ac04be5880a12c31fdb51640f9a52e29d6194641ef46ea03650c93755aa92d4c638dce7ba9a4511e09bea2741b30b612a12339119f271570b639b8235aafef7087e2385c4ce33156a0600520dd85354dd99a3c3019b2d22a9eefda223e292360b0d60c18be4f632b02668a2d20b1827ab9f626709a596d6270eac078475a695816572b3e186ef6683f86a844f67c222b6bf00365a11d1b639d051b1c10266260659dc87e5664e00f8a289c4912b50b5e1cf6cb06205ce2644f1e815375896a0021c7417bd7b344425d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604191e7094348888ae230fd6e5410bbe0129bd63c732f5a32b775d66204c50b0734966b17870f84cbb40df64a355bf9096553df1f611ce2e8921746c622d9cd7be6dd1421c21cfc2353595b529080a61431eb965a9544281b7680dfefa04a92e7d431f878454832ce06d98d55e050dbbc652de398665ba4822490cdfe457c999d60a5318142244e94841556e4b6f93b5207684067d6146e4974854cfbf27467f345f5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f2b7e564cf6a40641861ac13dac7fc189da5d0027f6d3a416733811136ee926b47118754a4b31022f730400f6f10a41bd105a8509f61181035a89c79a715401fae5e6740830f8a314ea2346361600f6171eb170ec74e151da51cb656b25c5a6610f5945f4b87060b3c364076fa59b65b0122752046feb80b6ad6d1405f34074d9808ac7010846863547056193035c749eedc2f036b9dce2d4f15c169d08b2e17cf08283786716e71092bc741a1d37d122e112809896528311b264b31f60f935b4533b90d876bc9072e1df04c8af5203d98a55a5d3cdef37409f6024843e44a7c377905542c962279d68ff736b11eb20da2463b206ee03051759cd2702b101e642193ae3670f74512d90ced603cf97a054286ac0d4fb4191fdb32ff6c4983087e60c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419c0e8d736bb6e3a131b341d480844c15fdbf8562c0cea134a2fc1b60b115e234661a626186fc6992e916b4408eb5c57061400a253448cef46a348dd6ad024e03f8b7e185e85c80b7485a9aa161628cb546edce32743f13b75b63ebd45c4d7184604a8f420d4318f23935f752cb45eb071ebf6fc4a4ca5ea78e5f0c027e0f21f71967a7c3b8163c61f6315ac7520709b56169fbf0137fde2552759f35e3699fa1af6a13e6000e2f54b12f4b054b695e11e405ecb63eea30c007c84527c6307751646aa214d6092eb109caa1b0ada7e230b3646ce5572756760b228ac5b448cc4175afd3a54cbfc0812995eb9068ab45004ada2a44ec5c5426f56b27a7bb6246e4b8415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f642940714b21f2f853f126e91974122fbc7594b9d8c2d63c9df627c8de32652c2acac2f1a608634b1de38773a0c86047a7f8f2d704779577b3668051b01366d79fe931cfd426f7e9c46865975a418161d2613162d0f5b0843eae50420d0b3102dfd6c5d946b5a56921f80357c076140b9c95e1416a45b0557702f306296dc7ec7f1fa217f32cc25205e1f04389a3308472c004c107513711202247e6e8a6337a82cef5c9fd1701516da482e479ac534d8f5ce499eff310f9aaa935b8d8ece189402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604198f917f3fb66f9333b829774f6e53ff505aaf0532a2113f1602b0861adff40324bd17be55530dda13e7ae5a2a1b37345f1b794b60d33cdf1c7941356dd0449e34ac284356ca2be609dc09355089bcf050c833e2419fd29d04579c537457300e7062b44d73ce06b77ab4724339c0578a059d5f7b3c3728456907b4513b5740941941067404aedf375c65227313d194a84efd81b17d750ee35824031914f313b8591eaaea1f9e62f210ce1082085c3589364a0a935d0118f45859d6ef5c9214384b21b689620631e27884225237577d9e5ae6c7430549ec8857e21ebe7a7b0ffb7941f86f554d7d5d12cb326f2317afdd06947fc4247cf0d8461b2c0a7cf34c2479b0e1fc71e3972b794f6c772ede3caa3223d8c74f24972f433f45885762b0e454886bc44b0c231224bfdc1f0592f9a04433bb867784ecc76c5e00142ab03c416548a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419d6f4242a0ff2bf36b1fac55984e48c64602d9b35826153045b5b0021f8eee773bf1b916601bbfd2940e9a4246856831635b9950445988a66a4c5076c36b2d2248c30f00665f5083b3c5bf24efef9b6727188ed24630551237c548928983b577d9375887975f34f570238a9047b7d133b4329c63bf1816c0e8f285761534417258d9fbf651cd0827dc93d396d207c4e59aef0f50046723b19a6685e4dd0d34966a833094f117ba969887a12142ad6a15de6fdd60fd633f24bc8938a40c8d9c700ae69e02eef0ba753d1c645084371bd5c81d6e3279ef84339d11eda152e2eb312956062001ca8837e9af15a1d6d05e1584502a204599a72087c1d4e0b1e11e93d982e1d311357273fe4e0f07367bcfb4bc954dc675ef80c11b9cca02ea67fd550a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117b9d5bf2bbfbde2374a476d710f53254ed590a36683c90f005b522d748aa2d36651c4365ba7991f36b1e8a264d948c04ab47d9a4de437d64299be2c08c5098655953f1652b56cf819861ba02d107ecd7ee52258284ed34f4e4924ed17f7980811a5de665d3189a76f2f48e7220346215a420dff53171317384588411276b6b731a560e250a5a2be5677bfce0fe2c9d44cf6559f676027036507662459fdf7e23eb6f5405a1793ae718990161d7f56df1c0bc2af2c3c742545ece00d4fbf195066b5609b0646caa24ab59f4a7e1b4b49734b42545e854551795f04852baf90c311dca04e1ea86d5d25486ab044026f3540eae5436b351bb86d22fbea6d469fd03a1b4ced35e82a9624d6625266311b7a59a3ca29572bde1704d6a46970351d0a51cddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041982087d11a4b578635e93371352033639346e412df339790c0887fc76d37b097c93a39e488fb4ef5588e0061574adcb67cbdd6e0f7f03a677319f94520613a365040d2a14109b0b183d3e79783d0aad55d9826b6d70f6193ffce53e137c68e87ef4f8151aec93876124acaf749800f957087f7b14f006662778a255492399e3135eb69a35e3ed8424aea35109fc9b5309d4ab070db804484ab937f76b6a8cd57e29c1d311378fb04cd9fa4f093963b54756f33c62fc5b5f699a8ad74ff255d8717638707122dc9822bf95dc600df78265e62d6a1f6d8535283a0ebd541d6b445d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041915087a58a9d0fb287aa8910fd5ef8d04fbd9112945c0b74fd0ceef183d8ea77ed66a0d707ce4046034e17939daea580f00188a7c58adfa6b7256ce46e5c0412c19626b4d6be4bf12e49d0e2749f3f3047f53b1252b513e62bc77640f2574ab206b412349c8d1352bea0e6b061ee6bd76099fc02fc57a0330e295fa5f7b673044f1fa672e2d0e9e7c5fd22e0947220e144fadb52d7c2f4c3039c945190e689724b24458031739f7273ba6f459adcc1e197f92492c5571855993d9070e2075071c25a89e342a601b096826fa0db5c8e22386a900302b68497ed5b3e81c42e780429f456622e207987e3b648451ad768f6c7390c957565ea821c00097565dbab34d1b4ced35e82a9624d6625266311b7a59a3ca29572bde1704d6a46970351d0a51cddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604191bd8b533d6603d2559dd602818a58e717d7445580a557c1c14b99c304406a00c5ede876ac008ff1c48244840118b29659565e241e0548553ce4bda7ce3e86e799c270a21a8ace9596c2de478ecd14372b840c85441ad0f7c7053bf10b295065e7ec6245c51f0a32812fbbd2c6a102a32b777f06309886a240f0732317312a442fa7f160a1b43d03eee22b7155d95183535cf0c78d403d42917b75f7221c06a73d5eeea1c1319af44f5e48f2e7a5d5c4c220cd8699e406172dd504c32f2e92c77900070274bd1da23c22b556a4b80da0a46f8f66d81ad814cbc56533faf4cab65d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111769c4a84441a87c133d0e666213014674b16198476db8d61481e52b37ef20ed0f6cab674fd71ffa059b23665430aa2734222b923beabb83099a80c624324d0c00322a7734bb38ae231bf1024da578d209e268c84bf1e76b653a80f804ccf5d531fb5e3c68ddb8dd5438ef766937f26545c5db837a5b8098067208a66a142d914c9ae89a738b0f527de0f6246f9765b06518a5d73ef3e84e551c0b951ded65d55d1f287b0de8993841e76e7d757b945613d9d20970e1131164fdb3ca3a28fb257a256a5c6da780f6258f8b6e2f52c15e41ac2d4c1ef7b8983bf6afc90d68b9d20a80776a5d6807d37756483b794f94f433390e143354a2a24b286512454ebf7a645633b05dce153c590dbff850d66fb34c56d4ef74ca79536ed6ac272030ffcf74429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604199235ee0c5f0848107b8d615dbb42dd3ee491ca37afdd94213bb6ea29ac6f9f7de1e3954d8700772e9e707d6db2259c15a329ac7b5355122c7650b3346c868c70ad59726e7d65b1785e7e9435649b42505e1ed07d85eca0289ea2db2f13d9b236eb1fdd6c9ebe20106d9cab2188558b551f7e326f2d44b07d71b2f16a8f249d68ac8a0a0c3dd26954f15844387bc92b239e408d2716b8337de7027d056627cc19c4a9a663961e347bfa4fe65d9a61e75a3ff5c8047cd26f0ee44ac412fbec7134d2a583501ee6b81ba444757ba9fc9e5faa003830a9b09627c99bfc3a4c7ae92c42cb6806ae37b00f528f9a1fdd96e953e9c1a96273517e4e5151015e8d898d3a740057104e8c992fa5aae00ed6159477ae964e30e7613f62370a803901c64175886bc44b0c231224bfdc1f0592f9a04433bb867784ecc76c5e00142ab03c416548a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041945484239ce97064438bcba4e52685c5e5221765917f1dd75825f4879ac678608b46dc1708e139551b944375f7c476077fb328a5db0ce3a6386ad3f5a2398817113584439a836512d9f2314360ce9733543ec4904888e3c33f79d555fb4b5fe419f352e667db5b4791e56452ae622ef699b3bd8600492077dca85cb3072f3743d1465641b4c8a7862ed6e0f05d82e7800ba8dda4fce0b3b62925d6a17ff89032e12ab92713994f24372eb09517f6f224abb89150faddfe1094c8b363c526e49446c5a5d732d97b32d3a38f108895cd54a14009640269bcf4c17b1c7398002502d956062001ca8837e9af15a1d6d05e1584502a204599a72087c1d4e0b1e11e93d982e1d311357273fe4e0f07367bcfb4bc954dc675ef80c11b9cca02ea67fd550a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117317b7f5e031d8024c7bd84441ecfec286806507a4e7994686ff6e92abb612870b3ca7e4d6b94b0423b6ee771148307603419a162b423483187bd664ab4eaf40ed9ac2602f63e9d46ec154154a895804e6fb939440619030db71c102aa0dc1f4d3eabe5495203ff0efb917d73b6ff5b6d1ea5e711bccf02055a846e01ca3f8428c2799069897f090e523ea639ed592968fad05d71a98712460c08f61b4a39e6225fd0f331ceb11908f99eb346e082180f1ce6b42d025ac9201aa8fe4ac4929e1549afcd0cebd66b1dbac68957eb8e365e3f1bd36f1b7f473a0f346c121ed5ca38337ab21f59607a514fa1916ef1fc300ff2e54452a609532bec0d2e329fe8821f579bd32bb8ee4e51f9bd792cd6409909ad0fea00c778520b87c43537e205eb47b61c976b397d645775b7315039bb8b0038e6df21aced3a530240c9038b5e867c972492631e4a27004bdef306f0932249e3fa8935d9bb0430f085206a914d7d7def04e93f9daef736c9a4e72f5477114b981b7d35b0f1f85280470e5e682a945860f5b05925cb33112ea9777891d9b57543e3512bbbe5f42c4d02be4cfbea1330938c576080b11518c80b863d1875f450ab4c906ede645d453c89e201c0216924b71dd340b54e6141c310fc3e8d35f57bb959a171c121275170d43d55bf279e30292ef73fe1c68957d9136e3e251062389ae9605e812bb709966e737e193dee134146ea75e9a57e447a957c732c889b5faae0aa4afc46881602e0fd085a48607d502abe18320d03530727e70737c5af0287b64f3b5e315635b4b27c2ca67ba309b1d0d85f362a3e535ac20c591ae54924bbbcc868863bf0184175035d2ec71174cafdf54b8efb5848d9608458a4176516848b310ea357db6f8c75f6284a0e3d1cd66e4e1b295e877a3e97d50405dad32d36281a27b773924ceae75443804f7072ff8245465991772addc8282705d931020a90021c9d688515943e6804e76a0718097d131ff51717116f65fa2aab565410724c9430a2788d7b624c21144aeced4dd2ee2050d289ad4ff39a7e752be8dc0110904351d6571c1b56c5624b1a6013390121a95c38a0944c17d45b45b593a52531a56e485c31980f0eea5924b296f47b18b97308ab535a7bfd55ff437d87d46126d6b03be97cae0bfdff5178ede4902dd56f5213e154f26ec3455e76847d747daeaaeb1077ee2752fffade48009f2c2ba1ae642537779f3f42d67a372d978a2ecbfdff340969c026eef6005ce903e57e23ef1c129192df02bc835c40d3648f1788adfb7a48e96919a8ecb500ae94cd4253c02909fe0f38295eec300766bbe55c36721a705d52d5565907b7421910935c0501610c7310b324b6d68e1d7174c24bb00a066f7172721c0006ba456559f37ea2516f64eb3ba83b185cca0f3740261eb3f2e223ac58697930506623b37021290d8f3d274204770f2e219a33d1130a34b36a5458619d512a251fa25dee46882ae8255b5897d2551d25d57c2ff2b2be4ab7c02c03b7e84a0815eef3670023423a7007e901caba1f7d5c7a6e3dae0f0c0e48ac1a1f6f71eb2bc414dd2df47de072d77c3c7d82f8061ccee00621d22ff22d75ed382bd5f6a21d3a0a535c2e737b6b7a8f4a0b038ef64fa4dc551afa7a764214a2bf5ffcce4804e3e4340122a259412efa866e01c3503ec1ed4b0c9415f47885fe762194ad8a30219d190035a6811cf691232b392eb651f1a17a379770ae5290f2db55bc2352594496bc393f2597365bcd4875556b49322909c773ded3b36320e8250baefed92ee7a2df247bfac04e57f7465f4d4a88019199241e627ff0475c2d5e3a0c0b2e5cb05aaf06d1652f21460a6557f367d3672932ac3815b7453a97665d269e22897104e18145f019d31a73faed3096bf6e244785514e0f7d5537d3cf332711bc8f5cf5295a410653d2419f019f0a8ee5d4733acf8e443e05c946a1d814359e49c9354617353d6bd2855cc4a52004cd52353599db352b54239a075ad0004ef763b835dc388b5024aa7a2b403d6e67f2bc1816a783cb4330da044dcf420203dd15c32083b92a37a646964031ddf376cd10e4599988174fc30561765048a565b8816b39358aa06a8622d67d2e59043dd5a74f7b85a18375688c637eb635815c9bd1175bebb3cb5d3a1fcb2aeaeb441835545557b37a8e16399edc17665ae7251375c307939fe560fbc1884fcc5c9467d897172aafdb27049215f645e6e7c809dc52ff7cf320d31c35481314d08d8a6183e4e7006d380318183ef54d32f5fa3c485e98382c7f67181175b27ee06dbe2c09d8f343ea9aed1bacbe3b4977195c37f3682104aa31eb41a87c941db19ace7a603808714764a33914a8a82a9b02973f6ceb0f3ce7f1b4739e91ed2937dd4f5d495ba52185589a67e80c7c02a0ef4a4acf99dd10deed484e69bdc359dde93f469542e927ba9a7440688cfd7b9179ed224213ca68b350ac6029df6d2bf0944918cf742320c361fb28f58dcb31fbbdeb49ef3f2560666bcd5dc53c6a6b7648a6430c510648872b757bb7ac4332598b83693caf3c7adc5d8e2a5915b50ed3281365fade2a4bbe08271ac2d89178154ea071579bf1408c06bf4d69deed5bd5405753f814405f721ad5769e4519368e0702493b6da56c2046096c3f1199760559e01111be6a0ff44cec2781493e7d5bacc7594f91445e716daf2321d05c2079edce0bb92b2f211dea1f44bc7b6149c3e7fd00e617843eecc8050c8bf7c205cd8c7b247a7345549386156e98da5444f5b681509f02f2484a3b295bb577261416faf50893d0222e1c19267e8502702b20dfa044f239ba284eb0916c0bd8c056967312054093070ff9ef1d36eda78344866a47321fe3150533f0bc4b8c9929294259944630b46069fdf23d20824b99579da90822758a69236b144a1ff3bd060c4cbb4f2b3b705c09100d7e7e824c6f60dd6fe32d329fcd6c938df554f20df017fb90eb6860ce34039f08995b190efe158a5d980325cafc62c7e97721df6d2c4ca99fa5037479dc436c7124047a78fb2d49bbbc1fc90129765dc34e1173d889583b199b3e5578385a2f6e4242def37500bf30c34b88cb735967afa97300a8ad09c874b166617927428ec700393210fc53aa39a77c0c5d993c8e43437735e1567bde5d5d6bef534a73180067151501d673b2243b506e31c85498854253d03b622e1638b90666518304f598e32d653cce76c2f6384007f6df250ffedd7866265d78cb93283b25faff245245e82be4fb927227571b46d91c0e1e64923670a21da04d8428e71972b0210fae982d32c8020d0df82913372c4a164ef97cf46619a6e45486a71d4b79b0373eaa9d8f7ba71b140824901263bc0c491ab33a6b17c14f73330ca23f70e9657f63190dfe74b87f4e30c1014b662736b25cd87b096a422aef52f986dd4052e72e643bb7d650c92360160a51442bec10c27793a8cc1113f4142bdc96951f09ae52411359af276cf70a665bd21215be375123ba36cd195ee07c56a3e654500d36c8720d37592a5f528b03a180d431b4c5bb32106f634145671807bf22d8407666d50f94b054375cc77a36c2618a42f7931317cc1eeb3dcab6f158e28dfe32d44f1376d60f744ba3726c437367ec6fe6707058f196fa0ece1d844017d5930510930b30ee6bcd014bca621c0bea33174c6bbe7ce28c5055b4878d4c7bf4c34fb85579143e3b0945425e88382307b41d13c2ec58f981380456f3374c1453552578dec81b69885f6a161f3720f494c711b9ebaa197e17ed23c11ec26301e17a41a4610d305a9d0c1417b7ba4298065512267fb305bfdda1248ea21c20e9c88620ef3c2a33a5bacb1fa0d4cd034ec2eb662f18c22c40d6184da7851e20945f000b3df6d13eeef3c64dcb2355188300e459f589b8663ca0fb6eaf4f4562bc7f1f0622c92733d706646df090ff2a49aca2409bfb185e01d0900afd022867c073e10de2d51f41fc1ef37a1e774c7e43c7b3594e35104503d36327d3cd575162845b0ae7f90354ce3c68094e29276154a2f1524eb893258c9ad71c9dbf29349e696029a652fb3ddb9bb77ad909873b34746c7e202dd122f76f5e3c9e51385eed80217bd370d4042b058c35b93cba5378dd8439102a3357b50afc3af683343ff72201210dc9ba5ca6e19453ccebff746b7951017770d251d58ae209648eaf4671bcda06532f4200d2c89f670aae38306164f32a49492747173ce167f9631843b973a64dd340a3157a5a6c5988d0c67a1b8cff0b51febd716c4ce97140585105ab52f32becfa9b080907a739a6f8724b1cb91d0eb905720049d64a453a8e32273cd3e56a54f9ff7a82cf97128facad44c634b0790690254304ed2c41ff39351487360e0e525879170f85685601373d30a8b8c35de93696707e67a95d3c317823d6288a663b1e86369901c54215b4916b34ae377086b621208cf56147f7a17971614edf7bcb3fe066b668df06e8e5fc3dd8613848cd1bdc605fdb075ad0265471b315154e24aec70d97c47a0e39485b692c91f2150646d1272d936f2fb6080d499daefb78cc4cbf5dda7fad30399e2a542d442f6386912b3373c7d93862749e603685da5ab63eaf12b8db1378631b3d5c9753752cd52d665cf75e3e651e1c134b37191466cfa963488c731e3e0c9ba15ac225a9531736395e036048792d52af6ae7631817dad3e8315746a711e7b35436df25686a9376c2733e268309e7601100462e743da23d987009200841e8ef4a66291ad251bf6c2931b543487680c471752826ea3c18286c639dea8f3af9963109a6715a7921ef57236ddaaa4d89c8ec5a0e4a54216148c744e2fe585a2816c46f1e31ab16abb4c57e22ba7216e9500b4d5efe607b9fc02606711f487081a3093370c4cd4ccbbeab376413c33603feda310d7e33088147821c1b3dd95652112a0136fe0b3fafa8192dedbd5a4a4a00907ce46df5791ab0bb1686dfbd0b6c513e665d69aa2ad3da421bbeba4667e72c011b8850fd17989ce662af252221e8d14a130fc85843d22eb212d002575261ed4945396c1e6331ff5e7a179b590124cdbe3222ebee54ac33b3257369d948038bd932da477258c5952c5c801a934aab966c2ed88630352f65817a6de2ea3db413397329495f23da026a2ed8eaf514654ab30eb2a4f812d16eeb31312a443c22005231c02fa966ac0dc32e60590675b22be9254d7d90067d0c9f3f4b2cdc3cd0a00c305daac10d6247916c5bd7685ff841052ae19bfd545f4dea12694dfb51c956e541add536712c2d9e03a7e7ec30403f7960903ee12a3b66fd7d05aa8228bdfa651f57974d62636f36229b53153daf00d77001cd3d328d975a17f2335f6166c7186552f8950fc974f226384a065a2d180e34faeeda08e2af6724330ebe4b8dee042210c7bc52c2da6f1c71e46c13a5041b58168e6e0a106cbb06e7cbb338dec2e068efe8501040bff67786e68a4e3fe9ec2a26949568037a2e019ba9053f2e37bc7ecf46ab41e1869e355f838c12d4565735e3882c4fe8e1ef4eec602750768bf92c14d207178a9d7733e2816675d00c966d172e11001d46c144d1f3c743d354ba3aa41bc938c1ebc61199180f782e4ea05ce972da783ffed071c9f0ba0549d67f2a87045028975ca84356450e5b08b74078943561105a847c5d4da94f7816ab04347124d909088c4100529cdd6d32d7351d632da31a449556087225e74b3a5cd5557ce71125d306f104af91b74b1a65363d155b5a50cfb8cc1d157aff255d62e548fc237e18d726465d4bece6109a4a0c58864a585ed1866f0e0d0bf702e83ecb651b278057fc353f6bcf95175a910b3618ad82fd6366754a798aa69c7b90b8511aa6f4f2167ef3e8721e064366de4e5d0fd79ed010026aca52b8e2db34bda0b8566e09f45bf3d94b13d064295d58bc5a097c6f573d5cd8962ea4264640b70bab66c099fc61eb65f94adb54a6375747b65b3310124a8218315c76313976d90928780d3541440b82294e72c9ba082ae26d30e2c8d27de4223d412f32997c6bea2c68d9fa31679951d1628d359b316cd868008d1d7d5d3920064d8264e076300d9c314fddad0abe8c7b5e44a9d8371b6c0328d74aba5a3375fd23e5af990b66686d2b4ada57409048a05a72c8a4411927461282a3ce06dbf04e3f7b49953fe374e2684cc1f40ea9313c72342cae3c39270d2411589504c9eee655eb0d6125750b241cfaafb6582c19461554cc5913a475c31e91a7ef62245972594b7e3152ba04ad610bc4572f7cf2294d84279a6f93110c082167f231e31fa044a738c03ccb98930042e9e249f5a31f56ad1a834a8bc7287c34fae31af931004e7817a6669efb8a50780d2f7478b0ae7837ca05561737b1733c398a3be0b64e378f1f8e3f95ec865b83df024ba92b3d2f0330e92a22f4ef5e72f5473aeff11357a204aa4e0dc9c840ca268a3d3e4ba0254755fb41826d3a46cbcdce38bb48ca29f5dfdd2bf01d732886ab82733a1466368f004664da38a36eacd34c2800c1be46f9ce782a8986f33e08794a653d1d723bca58692739963d285c8e84106af3de07b28ece7669eeef6f725cb70f7095cf4d83d4f5583d713d58882f596310114a56f0ccd04cafdf877560ee25797d5c3a4328cc310d6c583a1b94fffc0eee06182aff3e906b5014454f9df338457200752e20810020f3d43c23b781133b1a14ae4b1758bb05457ad1154e24eb2ccdf6541e8d8e720f6058ad77fc42b804db3b12181d02d7444e53e77537589274da55450bddf3971f90cc6c251f8ca67dc29c15521f2312526cf67611c0367843798809151843fa428da6cf6fe8fde150c583a70c40ae5b02d5f5f73c65db61396324ea6a5e21a9660aff3b2648966d0f66a6d50f0be1754565816c55b054d6249d1ad902f650e31f23260d50a28da76b5c921735f3c57c7575de521de67ed61ca45fdb75c4ca11074307f24f1afc591fad0a496fb536ad0c705e3f4cde1177069d0da7565a533b7cb54b5203381e0171499ec5556fc66736c172253abe1c096b0cf4bb0d33a2742b7524011271f524786793784983ff5e5cfc38f517af883e630a75b71330f5810965eae129acc853382e25281cfe19402a70f4804c5fcf08650dbd347b37b5b35d4047935d1b17137a2b37d7626f19cd273849b979161ccf10d38d9612de1cd27d79c686333e9b466d8efb7830d76ab04ac70c7211d0f0af6f6447083f7a4a6660ecc83554c8009726be54bb2c7ebc312fff7b9278c5f66d2e6c8be101cab4000f6f08c2103acf082e84421e71dabd80787a566a7ba2445266b4cb31447af33f753ca6ed03c00c4f74307dc4125a57190feada8a1f4d353371aadc0d13005d040eb1400766d757be1d0195ef50cc98df3d6d0a1a0d2b8e195117eb3b627c06c4151975446f046c3a73f3d49d4adb1f5736fda515763be94e5393b3c8574ff35e792e81ac051b0a1711bb8bdd79e5035278ace7e902ff3fce46524e7c18c2532a6b29343d50e5f9de0db5782b38b2a22c51c43b8d2908eaca3f5ec9720aa703ee796605f82a2a91d417d6b3c00915e3c5281e9688363e9bfe4163a0ae048242ae3c229e87020bad7267373ae35be1e1c8131145da424a2f895c439a34141496c948bda25328edb4de6612c5562da62788183c946b00df7378404f973012004ca30ccfd8dd267816db6192192228886d0234f942e57ad1fcec79adbefa0d6839b639eae3cb6fe538bd1ac48e204686d74637342426654ba78047a38a4b73ce5130647942e21ac1b3aa64f1de5e6da624bb4a7a96712374e4491ffa464a552bf6e960c41eb717cec4820fd44632081129b566c51a9d5d3f343025339db416e52e843676a7e77914095142ee0e9840bdcfb30360cf4e59e59b54129efa3b306228f50733ca5c331937c978a7e9536b83fa4706b3504608269d3936fb7d8f5cabd5141b00eb4e24e8662132b94fc72484527303a0b7cb3c0e330c1d4e2a970edc9468065b41ea3d6afede156a68436e72aac60174d96b36787c287ab34c42767748453a72f12b21c446740b43c5e375e05e045cf0ca6b42c58d4e1b5e3a537d207a385e57803664651eb60052fc20594e9d6f39c486573b669b42183014151bcabf2a0de8f7fb300f53a22c3d1e4d3ab2a3761e6e78a53ac54dd871416d7e191d2b5f6b52d2fb0d7aa6975bfc6f21022a13b3259d828f4490ec021075a0eb60517312196a05f1404fb10642d2bd764e59a4b371b4c7974b932983245aea8d00c138f575ee81eb09bcb63f17b77c3270a7f5552e58f5ad78c1253d0f85962f24b5e3422ebd25ed23c646d276ee48aa58c403430bd00ce779c248ea38e8e56904f3a90f1e216c240e2ef809243fa38f299a14724c86590308ba9f8f315648c97aa1e9c236214c0078ec4d1f3c4dc728273be3407acbe61c39fe926d3fc1dd3d11dbe0164c0174727d52f7d41a1c3f9c5e2b9cb72ace03c15536579620d9a90217545a127be15151296aa67309cb62da67e67155067966fa4b138d466157d19a76b9b70910a0c1dd07d1a5637e2b9fc1471f146d6409f56f3802ae5324842c214922781c6956fb320904b01e750babb05858780d7c2b5a934f7cd2a7525a55f3169767210e8651c87a30b977728811277710b9024e30600031d7d45101749fab018d29a049c94c84004fa0b23ab686e476ae4f1b608910542c40092a40904fbc2f3b404246674f6e6934f31177acfc69132f48614e765c27122787e26b3d912740084bc112e1869c2a80b72f14416ce14a84159521c4c29a1c5b51883ddb0a364e3aea9621c7642d0f14490b1a8fd5a75cbb05d06a3514ec084df1754fe8a9996c23e72276a3ad285907fdda2624fb4e087cd5597595c1724c67d7493d32d7de75b8127c53b625b74b467883384de1b46c3b538b3f4877fc4b76ec791236cb2b21da7aed040953b24796af005399255c610a1f8e682e40a12dcfec5054b44eaa34f41f0907cf205726d31c0e54cddf0468fd53451052e38244db89c54bf09cd93c25fa651a3b3ba857bf549f5b8107890f3f406f3516150c7629a7ef04db5d4333e1f86d70a7a3720e833feb5357a20e267da0c46b1c81da2e2beb295007aaf6091768007d6ff30b076f4ffc1c5e752d67b83eea660143a820af26311bd8bdc6570a5da3236b124023a5a0bf7a4283bf7c7c7d01449f884a008d5aa3081d2b177e4fc1ab1c54d5355c80192616e07c3127503dec35e85a6015b8971133f1fbb5382cd3e93afc810e75970cec379ddd4c38512f150cce250f56baf4f329060b0306bf65714d267a5a7b4ac5f6443377fd487df636245008aa782308ec5c1ffe787bff48101442776007025c635d234dad15de989744e257c27ab187c35b34fd7139cd682929d803b530145fc44b816400446449f06e00bb0d7af4a6e54d40b55719681b22396ac4841aeef2220b9b03641f1beab80e15e73655374f1f3b02a26500e9202910b6cc253b950dd164f02ab54449d9ef09e7ee9e55e301794e56cf7a3fe3cf1c0cb0f35428974f19260d731f4788a6f1019b52914cf2e21819728ecc6edf35b122a678e031d446be7c6cd1e91a18909352fc17b05c9656f13c1822506ea253f74c5d4b5c6a40234371cc23af12ba31445280e585283f13d91355949b27b04f432877e95534c2c0322f4a225c2300e40b75bbc2096dc0e7de24ee464b31c059125879d85f5afde4f9750586c643673e8b2a8f32313026caad0338814274539258756bef776d38309218e6c1c972d466142e3db87e2a65157c4c22fd5911e126c82a698a8d22d94e6f2a5a092a4c87eda5540d118351b2f48460edf9e2184ecce97827e0fb4080ec8c554842557b085f1a4c9a3535590b003d38525282136cd1d95f3582dc45b79cee4985675e5a216b901c8d8c9c42a113d056d4273c7251d09f0775248228f08e980a73d3352b688cab5f06781754af9749337560893e43b24613e8be5f59d9ae2b49753bed0b2dd88b6e0034fc168ec8125a950a127bf7225529f8b30d4ec4369e6c59a378484e45b835af247d39da47745204b68525c6fc1414c329682dae280f5e8513562d898f9b7242f9075356cfc0787abe28700ef95e2e91f5cb3fc9331946b7591c2d23f7292c6d42ce6247f20e63071d2c6600ce5f0dcc6cc45e7fdc446179ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455247d9532d3c75661126c4317dc1527f1c938666124261a316ecaab54f58e611073b22385e24cbe50f97549a0d33d02e61b3cff7063715b006658c813657b0611781f38c344b73726cd2d6db24a9c88b302b4fe83ef5cac170c5ba1d788440c2136d706304d2e9180b2517a02fe3647377547b43695b2dc5099c1ce074693e364e84de3a75c20c4a062bd7b6426b23b0110cc4484cc5250f168f1262498896f245940da72bbc62b72086a8e12050dee530cf5b7c2835587826e8f9283b46ee175211070029b6d5ba508cade4568b2f9308718b1b47854dfb41938cf43139e1dc29c5237c5e01d9041618feb56f8ab6af72d5d8f163f47c597c1c6efa0b92a78e1724848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552819e9f75a28ed55f8a83274f25251c52a4bad73a2e058f27fab29b01a476ae43f1fcce2a70b0524fbb01da42affd9c7cd7369a491f664d1a0b1329413f143843eeed9547930f142e755076063129fe0d6032494a33dadf1e01688943015e6c637e4eb34226fd4e479a767307fb44d8025c62cc0da44b391961e4220facf63e577ce9561cd8ecca6321036949145aaf7b6e0a1f0623d9a9095fa6af50b7afd754834a29500fa1d473c21ae867fd87c31e7ab45c285ce0323b77f298778aced65111bd89739fa6c3456bd9ec7a431d436dff99e970e63e997e8f79c130da46d16ee7edf24b2412cd2fd1576b17764e065308f51604d2554b650cfb5b4e841be11e2a95993fe437d326d26dfd0602a24d3a7f8d0c2a95c2061f7ea63a7e6de5bd54c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455202e9b714c388211d6a4211534b17e23738d64a5ee8b44429fa8c8d581ecd7014edc6ad2bb372b0064bb12e4e5ff53d377a8eba5a5bcb456f45efb931db149e0a128dcb096e3ffa1a7695bb7d417f7f0d0ee24850b315aa4ffbda612afae26527ba34d24017dada304342b543c063e510adf54a595e282c0cbeff0d67feb8da2a56aca15ba4d4043132a4fb35bf4d85528cc2a418095d50636a98ce2432e1242731cf5d546a541374a5267b7781daaf29a5898973b9be7253f70df735ff0c7437ec9f2356d417dd44f05dec22b2c1d31b8f5eea390a10812352b2a9325468881863cf7064bc291756742c694b94cf56076bc3821396fb4a786f56d65ce02d78390959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f716f06ae0c14aad02fd4931b4fd6b1e03c6ab9f45c4a6bf732a4890f47dce87472da823f15f0e6ea4bcd5dd33b098931012fe3100c137aaa420ec7443dd22a392df0be9262bf490f13f120fd6b49b7616f706ffa2bd127657a9c3d634e4c546358ca55cb799376fb27d347600c782dd76e01489a6f342510664a17ee740ad90737cbd8b87b76750f459aece2144d407b1a113024104ab46600dcc05539d321b87104bfbf0d8102c94400ff8b229d3b8c44b0b5726608545f74af9249113a60bb3636bff43f9e38e8470ebf80688f918019dda84462c5e6647977a84c64d4fcd92ee7edf24b2412cd2fd1576b17764e065308f51604d2554b650cfb5b4e841be11e2a95993fe437d326d26dfd0602a24d3a7f8d0c2a95c2061f7ea63a7e6de5bd54c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552d4a0bc06d78a60428089156083532b66ccfb3805c742c30a9e62795890f86b2abda38d651197db507594b612bdccb46051202224ed148c50b6a7441d3094623d360a204a2b47d50437588f1a0a7f5342c2515441dbc8b04c9a79be29733cae083d143b6320e31e255a29216f95508376d4155d643398c6563f98e109d8ae7261e407b13e2a26df737e52a42d7689843eea3c8827339f4e3e6e295c5b98cada749b3ef45e61e1eb59871bb67c81619348c119e078f4e98455029a6334c1d024330bfd167926c21d4b04f147247995e725bc4444490ddcd932bdbd5a2c94502f39a033a070ea8710313e111a1267fa8330cf9789250096745169f0f94d3843be190959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71aaf092485751415d40b21d3ec8844906f8282600d2e1001cac52147ad565135430425c1cd6e0635d51ceba6ab05b4706f041c40939a10d3d6c37842552c31b0c008ac968d8f9474588cce139c8dfec7bb7e3b055b420ac7a52193e5f50010b0b6425205e607d287077f39b1828ace47776f15d7392b87b4b619f8d42acfb187b14d3504d2b0b0d568d1c772ceb779025d790760fd1b4fe5e2210ea13c629e3427d259b5e5e3d3a2e0d42c962b3c1976272c3ac6a41c77626052d3e7e1c4d1e18489b9231c68282621dd69768bbe6066ec4ae4842eb5e840fe80ac478e89218015af81f70b2fcc056ca1e9c64fae5b641b153c73a1ff4184fbe562c341c36e13379ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455283793e3769080a1f20d23a5e7b73a35bb1e237467fe6df734db57d3ba029bc5cf37f0b046149514a5100e133fc21bd5ce9e0d54dda31e928f0dd4734b79746244920f175b175031a58ee6b108530677222cf0910bc53e41187297966b5ec401a71e03f4313b346094d3372102081da04ac631b6a6c4a5128e7ba8d23ecc63a71240c4a04dacee34b8a0ea4092e7ba25feb64594b5a124f3bd042d85d6ce9bf0fda599e517d0b1a3b2a96545199300823e849193abcdfc253e9f963557157c42b97989111a9c5136d11ca920647d76a05d942df7eac77a868a036bc6f9ac6ca63b7591c2d23f7292c6d42ce6247f20e63071d2c6600ce5f0dcc6cc45e7fdc446179ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552d04aac32571d7b2ea22d5a7aaf33a70dc1c14d557b3d714ec946a212eb9c48647ea2f142672d871bf30120225505204700ef0225a1d4d17a80949d5c3e70e046d43379408dab55758a8d773a5501d752494fe639ecb5f71e320901000a613b19a8786742f81a803e6936b54eba23e65b45b0d37eb78c0c746488ad11ac89147d2ecd68418bf8d50188d8b53d0add7310d5d85e13d8cae770c366df0cfee11d3c09ba53057934754f6f98ae65463caf4c7ab253524fe7cb06f5029869ff923e41bbe0396df87bac6e511eff2045cbcd0ce63d216a50c8fc507ce2fa311ce4400e059d4f3ecca80f0a8762aa5093bf85795ea52263d2fe031db17f053f82f62d7262a0f00912b8a75732cc241b9c82b167fb2b2531111f897d001783078b20e77d2d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f715e025417572ab11f786f3d3d7def7649f1c78a3d025ae135e2db174c1466ef03443e550c8a62d934dac1cc58148b38219566386b87662b64b931bb42ee63b221735f31585d82236c847aef6fe4081a4532e095615518726c2add860a8ce0c9587a9f2232509492516e62866a9af85132ca38bf5e9425b92312247f1fab3ccc0f27e0c4766333b56e18e220753b5d73333ad3ee543d8ba65e38afbc263407e30b0f42a266f6a42f4d51ea7809a0ac6c053f3d5e1a434197072d90fa437d03312feed7063549881402555af9578ba200441dc70b567826bd2b0095e471beef0140edd6b62f26d7ba449f5eb17b92d6a1739c430c15a343f80db6cba35f3770d106713b6640e56f9d3f42021110626cda0d18b99c2e71e64960cd8daf0882280675c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71c5dfdf5d2ecc032d47ca4268b05c7562b4e3c85dbdac30223f6b063fb6d40f3d64656d21cb2b4e5d2236763643ab485c68fedd1074189b59d92223596a7ec26853ad0c1a67ed0d621250766d4c3cf31a8411ca32662d7140fc5ef73f0aa2877b708ba519c24f910d597c0d19cd001735a9376c0b27e15e4400280462e23ff93be120c3222b242863631f8e564fe9eb4883e00e78557a02759b6d541b11a6cc44abbbee66ca391e0b0d10a85ff461046574de896046905a685f90b51a8879af7ec35b29743c302a6df8cccc650a0925144b305151fd55a34de4d3e438ace77e7ed3fc8a4c54306e71987fcc114a9c2d6b83ffc3316e7afc5c11e4473c5726ce00aefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f712b603e4d66f1f74efd5f9c7c867c6776b3d1305ce1379c30c94be5148dbbd91d6989fa24a3c1a41c318bd0710d6d4809b398cd532fbd19610b5dca557431735bd38d9c709104b16e380cd15e36fa450ae50b8757461ef350c3fc014e8db27f409312e121fe70ff474d4f4d03ba574a46c8386612b52c392346052c4e3b4dac4b644a820fc39ce54971381d52b13b082cff369e284a97b404eec352743cb8083c3b886e2a3da06849ae2f1a3c10ea88764af92a0e57d73b31d67b6d291307cf3de9f6654ec983ae26575f8d0d58670a70242c8d484766ce715a9e1e19e05344396a5c372bfc35f00c1f8dbe61aa811724b4d27b0bd65ef33a9b6d2c42a4f6bc7524848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f2745186345522cbfd111ca4aa3654ea9342cd60a417d94a6fe0cb9129854d6061b4c0cefaf4ffbd92211dd046c5d1e4fe745622f2632fa21fe429690544322ef4866af6234484606094162391019b9b19225cc74512822d93c1fffa7156744c21539a7d8812b658a127a6c2e975fa027e87b4063b823d34bd158df475441097da1443dacaa6a4cd2d37df14d3f70bcbf28147cd6c12413e91d2b5f19467bc24c7c58d527d50e6d88cf7557c4fb70a00ca72ccc2dc813f89e4b6a2533a733335acd69b624824f31d05a2aaf0db200ae910c56bb32d3065517024838baf56d8b6b00083b863400c130eb47d505fe1321b3c6742bc896363c4fce16c421685cce77044f150c4e7caefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71ee15f40482fc6a64e78c724cf11dde252b3fec355720796036df53675673333456ba0c3a23624e782948f76278a8b40f4d4f667e6f1ed558d6b29c337e0ac47e8c802e75e3b3c34598f1025bac52941f041c184d28335c5ca24a124242f361332de6df28b2117c5de204eb0128956e02b6a421051538dd0ed85ed321a216dc00e6fbc744a4f85a55fce85d3cb4c2e55d3e209518f5faf213cc730c4fc733656e3f05d72778ffd72160dbf976a78ef9568aba630dd6ce9979642f600b02f4d57538680d0b5c0a1546ab44576ca5aa6c722f6d505fb0fff82e7c180358752a625734dc7b59c9fc267032ccb401dc1f344ab3eb4702437e2e70d797253223e83d031bfc5359c36f38279213fa195271a24ef08efb4b21e895134004841b6d2b2f041bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552c03c971072a07277d0807a26e77cc94cd23bf01458faad03e64c3a4418dddc1dd20498229b864c224de32421c821dd0d62cf121c7f8789236936b9587874550b0386f0323d704c5cddfd67126d353007a5a6f64dd1388238d578ac40e6d7d939821ef4539412a559f1329c59e282a2022e26ad7213ab8228ee3f46413d24b773ad67817758f3066f585eea1df4935270ab1c876f1dbb515fa458b70883cb886d7b390272b221a503a231dd75394557135d300e5f6b53992dd084c9769e863c1b11070029b6d5ba508cade4568b2f9308718b1b47854dfb41938cf43139e1dc29c5237c5e01d9041618feb56f8ab6af72d5d8f163f47c597c1c6efa0b92a78e1724848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552794f44704f8bec0cfe45d122c4e20258cd25c767d346486badcc5e2bf7837c0bda97a2465578f07de44830478577926b85fba768e5cea43b8565db43a57ff24461180e5daa5c9f6f1d11912893eb670bdd2c1558d35bab50d640ea20b6ad366371164a4273e5877ebb6e366996940e025e9b3b542cf26b143fd1895c01cd29402e9338600d8826427f450f56a568957855e52e13a78ff25cf3f9a41b4d8d4915d4e0b451ca4f453a519be758a54aee56c584de54905d2a2b77891f311e638c22eed7063549881402555af9578ba200441dc70b567826bd2b0095e471beef0140edd6b62f26d7ba449f5eb17b92d6a1739c430c15a343f80db6cba35f3770d106713b6640e56f9d3f42021110626cda0d18b99c2e71e64960cd8daf0882280675c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71a23b294b2d2231009e495c208175821a1d66752cfd05150a3167135783f66201777db67b04d6a604a59a76088e461e50cf19e8311f20763999842343dd0ef100a88bdc5bf850161b29fc2a2eb1546b5c9900f924e358ac5f3a678c287ce63a5d37268774d2511c68ac31341fcc5d4a2f328409087375fd37ffff974dfcaa641b29962e7b67c7367ce6deb243ee8c113453bfd64cc65b6e39eadf114494376b319e1a86436b911574bf40cf352dad571d10cb1d5069beb778377a5d169c98e27131d05a2aaf0db200ae910c56bb32d3065517024838baf56d8b6b00083b863400c130eb47d505fe1321b3c6742bc896363c4fce16c421685cce77044f150c4e7caefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f718fe3513737f289298067a04746d31f53ea250623e928867c1714054ca061721662f0ca7d3f33890f24b3882418867629e1125e73e371183cd9eb932deaed6a398f5008391285b56db5742a3459fac423fddc085c36de614e5395945f4fb501145101493e04c2322e7c82f11d17d48458fab4f437aa56c968c9be1327639f16016dd53500d1f54713ed5f69305401585f611e9f327a3ca16a1410d7562e169548e96a254c838122354853af4a37512c23ba7f613685c0a70f407ec17e34d5bf3c00c65506926f6d1bb0e4492073f57c3b865b802f16c62c69bd0126059a6b6b2a059d4f3ecca80f0a8762aa5093bf85795ea52263d2fe031db17f053f82f62d7262a0f00912b8a75732cc241b9c82b167fb2b2531111f897d001783078b20e77d2d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71e9d94a77b0f44d5922c1c15048cdb643355eaf13eaea07491e0b8447aaac9677f127e40b9c7ad17593da1d508c38711b56645a64d9f57555073dfa217a5d28068fe1831fb91323784e233f68e7e98772f2ca4f220be4156227c250569dfe1268d04b4d56a0482f2ca35a312346a4403297935a69c77a0475e9bdf16de8da6b307c6cf93d605ea51014096360ed9c542c2a9f7407c3a1ce5b8b4afb1dc4807a28bfd85d0c00f46261523faf027fbbbf76a718687a4e39db6ed20023374d59ae57ffaba86d9ff7457c49508f5b72b5122343a7ab44426e4e055eb5912a52747a36a033a070ea8710313e111a1267fa8330cf9789250096745169f0f94d3843be190959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71e080136a3cd3d865326fba51dd9dc919e2fe25778560d61c4dbbb809139fe416553e54494f178174c063835acfa5e45dcd8efb69d3d19107bd282c176b5e8b511c637179c8e61a3af4d7787081fe04701db7ee33f094fe0b7671382510051737225a465274e4e05df69e981a3a58942a12dff93d14d0ee6032acfa6105369a7de5857465c929af6909fa322f66410924dfba3b1f8a9e64007aed83327e3dfc6a1b69cc54c6ebae354911fa4f4d97e80194677a7bdfb9c8362f467515efd8b8251f84be1406ceb3595215057612ba396032fa325ece94af3eb25e11393a43cd204a28ec035167113b2d8efa28094c427d7737672288c87618e07aee1f68f30d34aa71f767543c5a51e09d864695cd910d28a172559491317bbb3efe3cb1f901043ea1673cf7df0511fdf6353f360a7926ee6c2b088d1e8c402293bc4d7586777234783a0f45525c24534b8c6955e96e4b8075a41285fef33498ded516d8fbc81d52421a2306649c3f5a273754651c1f5c1554be759c758858268e362bf9c53a4356aaf4579359cb001da42e139078721ce93a7d029b61d92f8812b269a1e2be73c96c912974d2681b2be0dc7916fd293f09b0f3446c363601e302540362b39e619579ec553a42544cb5c49b6898233257d5f9b71938c3ae03a6ac5b442e94194004f03d6f6bb0b30ea05aa612c4065555d345c54894f8c72949a87f7d344540131a90d53b6527c4203fbb322cf9b73b6df00f6534760bad113a7d675d5131b96491eabc04f601084e9e15c25973c87615de72013a14a5ef063e8aa8332c2a2908f3bd87237eba53371350db6d6788d9245101266ca51e9c160d5c196baa22a415c52def45c9f1ff76cfe0c22add649f3fb90bdb6522c67d1d0cd6896fa701724e9d3071511223580749c65031c83e5e2d5378187ef63d061cd7f60e604cc489244b3a2249c7611a42f59d6c73de04b116a7cae61e7703893914c64a52c3ed324b89825f42525e185973db6e2b1cf7a6704c26545f470fca297422c804d310735f9f886570bef977141023d07ac530644de5eb784d21a9f1206d64d93db987381d384a6a4b58d90f390b35a11a4892af0ca7c19d64a1cd2831429cdf0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b999b7c9b0b7d7bda57e04fb076f33f59e4c752ddbd214a7857a52ee21eb74eb89bab1ee1c6d04fa13a445e9f1c34600add1a58a0413741b7c3b565015dfb39a027ca19fa4ef51ac39b721d3ad4c352f18da43b9c134057a554413d0d4208530016d41eebe0fb1a55a84a3ce2bc4a50b1f2212c38a40738c9fde845a7b7493c259c817e34b0054d7d84a301d18e6a00a2c75575ce2156363d09f30dfa597f397b58372a4a84881b41e26e442053000a9070ac3a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2d3374e78cbdc6c5981dc7d7112452dfcd1bc2fe5ed9269ae94c8155b7f7e63b9477245fe89e60f5b714373cf3d0a711ce7597dfa5103285099817691933532138e5d4c3c5dc0297d38696cfd9a4a492d88be206e8f804bbaf9431dbbea7e296b4fa91f4f72c1108f05a5712ff9a7686eefea6b1f485276bc29902a16263d61edaa1d6cf631877a7810a64bca40977e77ce2e71fb323c24d46f23436a5a602896c7e032a0429b78d4216b326ca034519ced5f4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003248cf35f9dbf544016bff02e21b130809370e63f2bd1109736568417723ae295f43c779885db708ec0ab52b44b57b3294625b43779ec709937e1446c4a7c8350bfc5d0740663277d416d6416dc4092c8f92c56abe05041530263754f9a92d48238c6701e52efa63c31c7632ec9de73e87f40e34bc8f3d3040a9031b0eae417492e74e1555983f0b16bfe306b8ea15126a93b91d81670903c0e5416be6b7d7635632ce34b9b9473999b6dc6bd5f864686d2b332e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed88c60308f2c22a4e20fc4bde53db72bf2bd94087c04b174178d53028e9ae5d87d7031453943f1354aa8748df9cf61e349f070a3e2759355da09341460c744c7d5888366b7d7737ef4d440fa6d52a74854761769ab0de0e514aa9028f8f7217c8b25453cd0b1d6045dd7b59f553a4092dd65476c6676c4a908c314a4cfde2701308383d916afc2a47b5cb037da91518bb7ae55fd16d7632db3d695f769b5c406c257137925c3d0c687d7a6eb65ccc48ec37311d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eea5c57556330656867ac46c7d95687aced2255186d1a5199e95781d6ef8a62e6a837b258d9ab6050b0831108274f51fc6274b1731bf6a2f85ffa44d433bf9483aa7bb5df55c1b2171290d29c929fa24a639fe7a8a97e961935f9b33b92fbc104c72ec538dd9c64293ea5f28cdd3fb07012f7e2d774cb3508be19a71b308df03adc1305e60c3ec3bbcfa6b2004373b4a3d2ee021100cee2b4bc63a1e99c4887a02de5c140c364f6d0c5ff5268cba6f42d293ea6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8df3d07666fca5751d90c4a4b297e67b4804052d20a3a3d9907255f945f756069988f19bd3a540b1020f56af0fba3648eab5879fc1ae60c3c29203c7d8d375b0997f75bca766d0663f74538b44c6e0561c6860861213940bccdda13752f42075101fc35c312401473ac90560cd2890c9eae2f02a9f0046675ef3f5cd26850313a75885b09666c6998168d495d21d81ad775170d05b59e58f58f92286f07f173408eaf4c00ed196eaad98f318e6c58175e2b12050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f7cfdd4306bcd0318f70b90041fe5b016a47e96536a1552f32ce7c61ea62082b5d55cc0c77c5bd3bcbcc870300b5dc0dab6c7059baaa703196eab857c6830f33285c355f4b849b4524142c3a3b8fec26b7f46d25d6715e5b6929586e3aa0fb3276159765e090ba04d87b9803c1ab1d348e654d605748e34510ed6c36db4cd40429d55024ae82e26a08e0414df4410e1697db5d1567c3d90691b4dc4af2a15f1a11bbfd13b133a302be1ee80f48a49b0d2488b945000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059305f7bc9f099201c83423173c0c336a009bb46f4104a4668c94e2882425622ff6f1a4b5d88a40380a397681f8acc38049846771b25df54d4e73748fe46bb3ad264b574521dbc0ba1aefc751e40a864229afa6fcd761b727fc8156e0f0a6603c35bda5ee2675b60e5dfdb6efcc4504f8578a51472aa21015880a11a49464321d089e10c46048323f7ce0417ee12c845307ff1191804bd500e699247d640ab2cde830a222daa172f6c6fc00b73ddb67d370c1d2d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099e5cf602b992574f136083ad8676a2b78c6a30e9b02ca043537c9438e425d52205a3437f087cc5556b3112a3e125a08d9d4fc0e3c2d3a336a60af60ffcfc34618282000aaf13208c3eb8a3eab6a291922c5ac108e3a537740650826cf5c681db352643a7a58897ed3efde49770be2277f5681618aec7335edfd601c6fe8837b57abde0196113e004beff05d56b24411a434bb22bd38437b2691c85daaac980df2ea202b45c8267d8864ef5294029b099ec1a8640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e062d35b58895204875eef739a9dfd60fb628913e1a7383827c3e852e9354f1681c1481e16186e446768b42a5fdbe42edf406b188d31ee309f05627c19cf1a67d93aa36e9a7cd773c270023c475c1305b7c1f46ff3f97445e94eab3f6279274b6183166d294164430df3b042d19e407040f6cb21667d7d1bfcfa992245fbcd13b32ca277e5c4842681b0e0250854684c8cdf7910d257aa774eaca1403c0cf75efe52ba07f283700b07eae4324ca598697c9d4f310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef88d552207592165e6fab442b97750372ee784290a71a7b9b22412f6e805c011e254360369a131008605f23a37c9b57d0741828824299700ed3a627b464e46320749c77eb0c6e48fcde81713d43150843a39e75db89590fdd6aaa29becd5a7adde27133262a60606900027153b9b20b4c2aaf747fdfdd29b793904ae6b3025967372b051da7d93560e0417371f347684bd4250698fa292c4c3cd74976b2ee2214074965aff90754ba69513d5a4ede7dd3180c3b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073245e1a2ac7a2558656844660709c02ee57cf16b40d08573f56416cf126b30b281cf02775b8322601043848df0dce59de5e0e3f70479776b01ccf0c36a2db2bcdd91b4fd7926722275e153147d0476b3afe9c577a034a7bf497481b96ea1a5397897c75d6c5e644a84ed6333ec67e6960d97307d68d407ca950a208da1acf4f3834b818a630b02ae3196a4959b799035867686cc5f9652fc925a6667d087d77a2a24d38b90cfb650cc1c05c8f4e1e7d201136470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf27fa480d40274e13d8bb7a524776090ceeb96639897e71a4cf3701af8f597e46a3cf6a4788cd0e06acc675f277c221ff1d404e46dbd646617d8007adec6f12d3edef2a358c165afb6197753153ab44100b88053540ec1cb1133a0f8ff2c56e5f96545c7ae7854a7da03b04c4506b2072cf71118e08813247ec6253a446802401c8304f4ec10945659cc211f1f170296a260d09f098613c4e227247090c9751a36ebe2adea1d44a72bd2d5c50291c70d288426e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db5e203702a51c6a7ea9f30e9af1c32f45fa9d653f96a036be7acf1154473c285f4f6251ea10f013cc13c67b684ef91dc1defb29c9de393c439703540a8e6f5dc547d409ec92a755214d9e41d15d3c4d0b97155dbd4f122b1e47573d3b412f25cf1a773d077f732f4da01a47962e8a0359cb62384d3e2e6236ba803d3d305127a227237955ed4a38b167fa7ee8c07441b0dbf965f6743560e678d859a9bb2176bcfe1a7ab974163bd2c58c7bb7da184d1203a94100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007baf632d72ebee2ce5dcfe269b8b6849bad77e7e42b91865bcc5882561776d4e786001472944d4504be7c94abfe7c8078ddb7103128f3611698cc1033b4e260ef9308059b5bf2b36dcdaa92d3d77442f21c8fd20368a6a15dfb82324887f8b60d6f2556c85e816744fb4d40412db7740ca88fc2a2d52de290e1829514b702a4c1e9e98416c4802242101e8070e527f4a1780d11f8394ca1151f65b229c780618b2d86c7c73f3cb20c4e770087be5b51b0a2d4a0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000825e5454e641c0136f7bf65ff90aa221d0761f7d6d0409071bd721424c109425921f4168495ad127b4c9507e832ac13161e6240e49d79e0b29316571dc310a6fbcb7413acee1266c334e6c352025cc72df499c0381616956df9b926a50df0f26aa58360c0e4083462a46be2ecc236563c5b94e74e178a836c5ee293eb5a91c0a3e3d311cd27cf1715aa87d0b262d7b742d8ea25a78e4fb24960a4d050dd82b7773eaef42f403647b55841c5e6de0731b6211cb4e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007baf632d72ebee2ce5dcfe269b8b6849bad77e7e42b91865bcc5882561776d4e786001472944d4504be7c94abfe7c8078ddb7103128f3611698cc1033b4e260ef9308059b5bf2b36dcdaa92d3d77442f21c8fd20368a6a15dfb82324887f8b60d6f2556c85e816744fb4d40412db7740ca88fc2a2d52de290e1829514b702a4c1e9e98416c4802242101e8070e527f4a1780d11f8394ca1151f65b229c780618b2d86c7c73f3cb20c4e770087be5b51b0a2d4a0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9e990018f5db02e0e51ae320774f07e622b07391bb0383744b9340763f1ea551ce54542ab070d743e420713e8d61079c8a90659470fc0197119175f34a140179118dd7896d226451c875d012d7dc57cad8ebd48ff21ac2664509c05b1a9005da091d066bfa552508238372d37a36a669b1e8b5ae92a3930d14f717df14e1801f6b0915867b3bc2f7e731c2e97a3f6590ea1663f034daa1bf305a30c9d6e3954630acc6a5064bb13b98289464b5aae772171187d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed72410304b1b1556751eb2a8faa6b708c5e797eed380316a3a89b0d400f00318bf0a3370a732326917a3b73f57ef40f623fc2701ac4232b9c6f3b3ea2a9fd079f7c237b4c8239767a4a0d62bba6147d90698d120b7631328dca38288f4fd901fcac9234563f02206725713cf6f6ea29b2a9cf502c030a533234047cd2e15e796e89770a06bce14f7aee811a0f89560307569b36124c5c3eb0b6de01a4dd520e91eaa81aedf7b925e5a462557da4b017fb67190b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051dea45393a63b4558b3bb77d8f4ee6e0b2def6d8f13fa7aaf5c7718dea0cf6456abec5ec3e4d426b349ac5a5353f74512207e29605c8327068f66798199e619f31882510b4835206b374833bdf380085c5cef2afd5c0a1976613c0e96c35b564691c74dd38ec85ebfbbad2ef30dd113569d837cc4b3cd0d1c2970640f85c257a79d8d2049c005387438c651a61b3a1b5ba826128c3ec670ef16f16c29178a751fa161542267cf5cc74a4e03deab4a3340a182400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fd99965b2fe95668cb9dc57bdd5eef3a644a7a20f6fc19339a1af80b50d43c4f60da84021c2fbe164b0db423eab2b97226df605bc1fe671ca734575abbf3a5093742046a136a5f013c618d66225c6c2af7a66d4dec2e8b50fda12b26e889313fb24bc17023db423f3e92ae3205fff971037a7d7c787c1f39bb57855aaa0240513bda284a1698985dc11797099e66583240386439b31a48498730e553c260736f825eda333e01a51771aeb1435992bf5ac429d63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023be082739b9d37c3ea828714aaef534fc54857a4fed27244235d2602beee31eb3da8e1cd6c5817ec19c9914691455300384c974bcc04d6aa8e22b1fce194741dae05c4bc7de1424f81f104be615c164ce7bfd0d8a713f0ece787730b9cd7c1f2182a66060e3915bd0ccb930b800765894bdb5639bc6f829932759548285407d5e888329a28f201428dbb07cfb5da4774b220a7e35a1c753b90005592be505015367a66415a27a0f0b9f9461207c2b5bf555c4370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2470f163edadb44a5794248c676582f623c861379d42d133230896aa9a7df3389318e6226abb550421a532c53b9437c24db7870826ec43fb4e03c30b04ce20adfa54940bd78ce403a15be267daebd527081794f096f8409ce4d6e099a2516467d1f70461dcc6e0e8ac2563d203c9a471723804641d9d571ef44133a26709245c7518b1c226af41b20e58529671cec0c6ad93c25ef086a1c9f0fea7a0416851f2ef8c94d7fbf6704534aea512223e90ba66f2b3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cd440676814d03b8aa1be0d5871c579684a6268062b01563a827d24c779cc051cf5457ef9cc9063c7312e2a09463313d61c820e51f334258958da5dbb161e2b189bff22e1e2384f92304e3d7d57d86134706f2a8862c122a98bf90aafef3e79ecd79e2373b37e6c407b4f2bffa1f62710a0fa472a30126f10bb58143840b441ee0ec9183872ac4b2e5fff55d333397784c35956e0807e6a1987880d5f05856a1182c16f5305792153bed2637988007057dec0230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000693cf00cef798b0074fcb041ac2beb4a2da0c52a8e9edd6f2c4d4e0165b50d188d7f48617d02fb088660102bb9d9413b232a8102a0f73959434729079ab602228fad5f37da8f3c563732c235e401f97992c4aa2e01115b10a026fa7b3254265cf1e2df5ff22b200a59d50f51f26a1c146ed0c5541bcd094b3d415a552d70ff2836a766664376645d3c6e9265710f8c6b420d4d64b9b018621240e77815368f519005ce3307221e4540f2cc375f75a820fccde97200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd7b5729bcb077b0f8b926df22ba83ecdbf59138423a1055e448a7bfac62a1248e79054f3cf8739607da538bfc0490900c0005bec9b2d13b718ad006b7bb64f95b8853368b3055064341235e7c5675b7e1b9451d19df22db954464e318c3a115389006d11eab4006ad9fe6764c7b4547f4f121fe0d5962cc918663c51d7c5321382af59f7132a7161fb2a5cea8f856505a32776a08e36224ddcf60c0120f751cd98960ce27b01716609be4f1d39da20078aa7290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a442003fa691b13fa045874c98d9ef19df88a21329083778e66de5051e7b9248724c272fadc9067c69ca3010381c0d64f9b526425860705d451f9366d2365642cf99851999fe774b1230dc30d9036f4384a14f4c7570dd329bd9984fafd07d445a583a3befe25a1fd0bf2e607ea0a27da8d218597746117e0116030d7df8ff54dc17ea4ca44ccf5f8a94eb65ec29363e63ad187b3f068f1583132a6a6424483eef5a942684e010069466697bcc9ce35dd6960a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4b4644f05e61312637b8a1760df4e4aa431b624c8bf9d6d2c5139311049c37e3033ad79e6a58d123470fc386b061a52ddb33654f8ede406342b87384553b64987ee5b4ad441193826749c0952f9e73e6e4b766323feba1d9b3dc54484912139e3c2d03e2474cd2b08502b5029cc4b13b3061a06365cdd255b5ea02667886917296b8b42c456541310fdad04c7c9112f7993d40785cd93601d17cd10a705d805a922ef110b1aae677f7e956e9609971f05cb7136000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000072fbd50c5544c813e16a500baa049b1f4be3f5110da420355e19975675ce832456658b1fcacaf867b13dd9736128ba5b4032277514b6c553c24f727730ce47542b762919823b2079be9ef843d1ec8e2024f9800f20f6eb388e22b850b3004067c5629c4a80e15074d3ee2e2ddca11a501f88ba4d4e0926136ec5a24ca613c5355e5aec001c5ea24e5f13ee2bce4fd4287675e63f625b780ee6995d5204c132068f16714cb47f814f47778310bb6a8c7566ccc5430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000807edc1bd350ea53b198af14ddf6fd505900e368ffe6f434491965480276f1202894ab6f72a1d43d86df0d1aa7e8b70cde6fd660c144887eb963cd4dcf1faa3957c84250b27fc35354f2f40ef6afd750e1d1656be91362622d2b28019395055cbc52d177ae8ee0395417880f0fb7d46bf372933063bbd660077f057cc6024f2a7382790b568da43c6f265337331adf61f6a20150b07c964e4d7e6f25d69b1f594a6d2e661296217ede83ce4bb5b5df766d1457480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eed7bd142fff4c2439b95f587088325b9d54622fa157f372b117fa6c86555f4847083506ce1ed11153d7950eac2f1b733cc9b0354c802774b235c0050b96602789112677b35c5e763f0fbe34bd35b103b7ef321b59e5c914f94cc073dad48d79bb49f92d07bfb35532c7463f0e85ca34c3854a660c2fde18a661f434668baf7226ce2c34809c6f2053be98720c1b3a2ea807711f454fa5081ac19c20690b12285836985ae907633e77bd0a07c367224dc0301026000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012ad9860e367bb52b2f13b3459e61e756b6cc127797bbb5c13647302f135710acf3ef022dffdba47915b9160d365dc55c9cffe69e03e8819dc18ae1853b635710d1d6e47a8617664639fdd13724e203ba65cf222182d452b1ac56e11206d9527aca3ff2544068a7df1f7075aff48fc2f68f1a14a48ffa3783fdec60c38438e1963c7a802c96f81677af95b4ef9a34736f56c071717640b09513c4b2ad0550a0d8a6ab5601f7149258a4803589c30e705e6ee833e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b40414243bd886d6d966c780fce852b85d2c560daf0b22c65e3f855b776847daff2ca6c45f87a131761f15eeaf46771cbbb63737ba14251057360438e75441a8c947f2d14b0152c73799571995e7e04e0ca4d0641afa159a93e7a2eae42f87c99e3e4572fcd54441d04c1731776206e7e2c7063be610f04c08ffc6a5806a70892d0a339de2817371c52224693a8316d33e1bb6d58ebf118c60b65449c7d100d3f87004c70a19209a1abc245da73821c61afed2400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c13f45afd49e60d62ef54610d05c728b9121d4ba713a35ee842e7230df6815ec32991796af23c5c95e6a6344932804a65a51e2f31cfeb6fe6394a5e303a95259a47bc4d3ffac154714f0932feb12a7b1f0a8b248ecbbe16cd75ef06aca45028b419ec6e5a166566067d5002729e8e38ec5cfd274f4efc54231e9c6881430934cf69aa70b30815707862d664b9ab0f04dce9e209bfe4991447fecf7863c03216039dbc036e67ce0a9274771ef1c5121016b05f2600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c7c181040910233864cd12dc4da9d311960ef170919fb703b00df4643675811207e192fc2165c386f76d10be3b93804426c9929836df64b6c597d50fdeccc2fcef3337bae223f2b87306b703c3187178703716b3820375d8b566b4df6144d1c3e0689762359500204da412a22984827ffb5f84d91cf9a6182bd0b1fdad5ab436d23ef7a2b2f8f5d60edba69bb1abb41df37bf69eac5a30b12863623be0eb34fe7829e532b668f410a7c911bb44d4665d16f930a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac8c0c5050f5695318c12a19f88e5e716e69031b53cdff4c7c940b66745d5b1919f8f96c89915a5dcefb7a758e66c91868f98b7092d41818ef626b3349aa5c30ebbbac3d98d7ed7273cfa339dba8562067d98d0cb579cb61cec4b10524f45e118d37584a4ca5140ae08967679bd65a4e9c982534859c5e1e5da3b06e446f14731ee0170c43767a5d47b16a301ce6513e6512b371434c6053ed594f72f2ec24316af8bc43d9c6cb0ca57529369816e9181cb4074000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b9428541ed9fa063c7f7a689c9b2924f74072113b3b935e4b50d37d1a5f5e6f652f003a736fee1215bc353aaf535268f9f629215642734b8cf1381745ce6326608abb15dd34350315e9116439341657828dce0e6802462adc7d9610f2834c141de792783adbb0791d599a4b17b5520f0148e551e7572e3d1acf47491fd31a0b32eaa63a3c02f50a4d2c32516d9fba2d0904332154be824c834c387dddd39e55d60ef146c5e8ae405a889740d63edb23d760c82f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013a04b561737a64846edf141b5f1b20342f03b7bf9466933a786524a3e6ebc374dd7773f2cc65c79f087d66dced069343a7a3f185174fc5e3c52e579d744f44e53e831614136065a439c225eb65d051ad1331371c7f621122c70ed7439d2bb145d81c06d91636b2d71d12b158083753b5e53a443ce8ed96f8e6a0a3295e8dc78034e5851d3ea7566d4f2c61a903e21052a9c79463aabf159f4de49076a2df6213d4e7a586137255850598a4f87edeb020027214600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000819f663c67fa25bc32d613806ec15439165d51a21db392302347c6113bd917071c740386a65b710b083ce2e3428683f238a7078f26301269b339c33ba43ef19cce7fd5994b3515c8944aa1da160996a3bdf943a80818e1ff002e57ed3bae109ff397765328b5d08e0a3310f4e73bd59b54cfa55702d7b2679764e28be2b9655784b5c6fd882541827ed6404bf48ac6c05160a30a206bc00d3157d2ddf22647bdb89b702086384161eb4f0612298e03794ab095c1b671e56fcd05d48e519d36cf055122067eed80ee29d7c0c530a9d55885d140a9986fb5743c4ef73538f997a2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc067164c234492fa20558aeb327f93dd267d9c4de302c111b4655a91461ea08340c44dd586dd1b145304257b613da7ced18f551a41b57daf4630fb7902c3e92bc6510b3717d9b16890f2f438e7a60fdd75ad6be217c72309119b3363f1df86ae261b32bc45d03030e3ef66b457c6bc298149dbbb1063276a36dcf7d3b1776b1992191f6a80b86d4095f6fe5bb4fa582251cf8d59e3f2a13b57635952441beb35d7ca3e5b7197d578015a8e73650dc9e8f46a3ac747a52d0cd568e7db43e4d889c0fb304ff0e0fa2040c66b95978f1fc4a23905fd65bcab9624074d8f840e9855d57f09fa9497c10452847d70c60c6b8511e605cf810e4c95b0de6710d629e9faf2962b2e647c8609a7ea25b2d42bbfe845e8e2690799aa4ae26e512d041c1c62f00629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a0678bb0cabf170506ae8375c1a0c285a23bdf706649b287d66ad2518570ce4273de25d2444b8a307aeaa4c5eebe7b15c40cf986f7755e3772e80802310d9873d835423774340680f7ff9445ba6a0fa75f280544ed34d9868d907884c1c4fe03966a58e21e901a7378f75514e851f2d36e0ab1c622ef89473df80725cb384a36da3dde83017eae91011cc0e55ff50062ea67bf44e22d9b677b2e33d3d7ade8e03f353d93b0894f12dd1a19e31724c9f72c0af5b4f0d915841781dc57b5de37622bb7349635a371b14ba417f2157cf6370ac53293df5dc100734949376b514443cf138be53562fd302d8ee6772131b74662edf8524ab13d22a0ac71f74be0e0d62e64f47542964f55e223147076e4b9f1e9edc25331f82a21327d02e46b140b112d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc065deb8f231298fe76f84254660f6db533fc18b718d2a64610f9ce2e3c50cdf566b69e7d7b540f2708c490ea1558e5850102eb21618159d15b4724a33562bf9104dd76591829feee3e67ac30139fe86a4034849a6a8b1aa8699774c11b3a41635952a09e194125c821aa013e7806ef2274d3bb4f5e0d23740c83709256b59ce86978c6121869cc686a08bda5089c237d2fa800fc5072384b1e20512627c9a8650b3c07f133540a970156ccc5097ab17a3d5c2f9d75789d644cc667f8677ec70733ccd4bd41782e75402dd87e5ed47a142317941b0a8ccdf03724d51f1e5d9abb4009542d3f4ad01a1f00c9bc5aef928063db21ed1a119b4b38f3db68186faa564b26d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06fe603670b552b419d8de43359af61437f4770f0ea321cc389941923175d48234e17b7c73ca3a0c3280f36073ba23fa38e0ac0938b99f9c6dfedd3661316e791ae2271d241331ad0a6d9ad44c442b27119b6e95027c810d70aaddfc025f1eed7de5b07800a0029150f263d669356d8a5c2b491114f0dc6438c0623b1ef43c0b62eef57b3fa1092303d25e033cc3e0e44e76e8190ba3b9cf4c688fb75bbe13324cd0de272c5504993463eca01d9f81ee0d2e0c1860ed8c9c1a21c78232c7a5687ba26c6235ce60191df7214e2c1352285c3aa3b033e889af62c807870a08ce85656546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0677c2fa76ded7fc57c6ddaf6ae470001f391b3d4a0923bf1dc1303f61a10b9156a972850d153ba53474d7af5b65f1505b0e7b2b43c7adca2b4aa886231abfa87468b30e7aab264649c23ca54bed704c22434bfe5cbd0eb41c7052bd051fadc156a1825665c7ba7531537ba319579a9f2258527914cce6595d215c493c9c723f09efa94c01e9aee33844416d4e8a14cd66a70849195f96b20309495d010720397c6b70954f42451945fc99296fe458196faf956a1261f607587671063f7350966612839c5abc8f93040b38475e94f3683e82d95e36d60f9a2d64db6b0ac3b48820ef0f8d1851c5770e9737b061d5f672453e6e4450ab4e66624e2e1b0a2f819a19f04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc060abee13b9a328f160fca3855da276b07cc56ca19f72397453274f625c85d7974c5d4ce61be3d7e190a341c4a412f7d404d778b71ca77362e4cec145bbcbfd769ac57c9234d606d7b16448f18acb24e635a631b05be79883455af25692776843988ddc8499b348c67bb285071827d5a54df3a7f76317d67588f6aaf27c70a4645b060d9048ddb844a890f9e58952f940f5c206476d618a71b5b9dbf0410576075ac1ce438aa7f3714bef4fa23483af64a0e6f74317475d767f53a0e7696939765d9477c18e7f5b345d5879d1f6ec7c85d2d6387012b0edb64ccfc5354845ebe6bf9d49938de2a9827767ed05140f6923f3bad39598ffcc146287d3d42b8a7e153186cd66c59bd98290f92b21821adf27a56583b5f1303954e4a3be47debd2c639f667e02e2fe86716895bbd0466726620c3d6340cc221a70a7a57c90c7936557b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a685c3374d19e7837949e812231c14d69d42fd32964ce720ed6288039c318892a4781315f46966040103d615f050cd14d874ed5042c43273719e56864035eca1822365564bdfd1c6d8448094d0ab5797d30709d2ccbf9464bd5212051147dc71cb0afd02286a39107676ee853964458583a636a212b990d0b530c4a24bce2f65ba1eb4443c70a1e7a4131075e4c90c10a70337d28c98ae356911b126fea5fce537f066f7d80a46e753d3bbe0d6e7e147862953f3b7aa9bc54630fad6156d28469ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a1ee47c1f381e204cba5ed2751267823bc05df2430191832cb5173000f95f3e5dd01d0b7dc0358407ac77c079ce8dec7139a9773ce8c4a93928465d4b2df43a69ef2bf82a5f42ec794edbec3dfe83cb6249c1666e6deabe538f3f89572655f61649505138a735c2377d4b1c4600f18848311b314d60b4da712ef892663e855c5b9760aa5e230a3b753acd0073aa69ca7a8ee2c04347437345e94eaf751d6fd40dc9daa5269223736c18c9305e60212a093e280028531d5045a19d6b6d16baf638cfba0177ed183e07694c745573fdc4156662a1679b003a4c462c4f4155f4fe606546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06ee32176d31fa0e340d5b9333670b2669dbe64b225e172c471f969175fba8442c21da623a0c45c650c3fd4233fc6df04fa9d10120fdfe540ba81fb9463107e51b819af26c5ea3a960cd07a524795c51746530241c965e090591469d3f1cd7255b780afa6023e2cb4d844bd4479b0b536bcebf652559be34440d1fff39dc9cba5934bf9f65b3eb9f7bd06bbb2cb0483760c25f1d2c81e6ee492860531c95183632c799a42d89c9b14d3462e40030ea31225946b00811bd397beacef5093f483c2341fa0c1a55f4d50855163a2693b9004646caf102a0d24d005bb0f9790ed4b22caaf46f7875552d134524e27ca466305c60b7517c2cdfe04620d1bb43b3e17c6a44f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc069f7cf0555ae98d40f3fd7c059de5647d67c7250b43dc890d0aa5970a6b4c5d3a45eba31cff54f07c81e586701f347f096de4c50505383962387f512ecde0ba1bc8722640e021a554b9c06127ceea1a4fa87ece2600a66f575491b17b1f5a6830f8a8272704e27d1801c9a71c9b069e3e21f27b0984db2f088335e564a78526314ff40055c4a82d16202eea2b71cd773fd8bc58578726ff45987fab6569ae7527297d056417fbcd06768a45308781dc4dda2d5f28324308569a0ead52c5be3108dcbcfd35c1e7d15dd856be6faf0fee16a3c6b30dde2a29518f15f05a1367df3749c123383dcf5b72562c6914090d5f74b4e0501fdc43930feb9f396835c39e79a9d4ff15cef1901448cd315c1f03be4a6c7fd00d996fc4147581ac0f4952cd64a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc068b4d58542fbd24774f3caa1d9d37d3192a976361c090da4f168e831f31b6732cc0606d12b005a6116012152ae237000982c9b50a18f58a0d8971a46527535b429a58e62a2f782f3b52a2e4046b9c825f8819a5231cd889071e60f043acb36f32dc9c8c117cc38a73ab728326a97c3b37e0c78707554d845718c1d5382d9da50d225766609ad7d711c626043499772624467a7f442c0c57668e9a7d73f11eda138b8fef5ec5fd7049a57436739a3c136652a76d52d502f21da101bf3321fc1458e8e67b7b855daf23b645d374f0103213a4e4d92d4ace8c5726581f457fe651152fedbc0b5959cd3419c0620c82fb7b741f24412b41e2ea57679d5b54835fff7112999d367331350a5b0e714a3ed9f4461715fc034e5b5961b855b07bc43d5d2df467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a9b12580f8a88b03467f6ca26bb1f0a6fada691295438f51be12b5f6142cc936926787f2a499123017f5f841ab57d54263f2d767c8dc43447514b8155a4ee247afd49a41188b0e26b51f90a7250111342be00d93dfaf019615574eb2e4fc9202f2e0875120207912e83ac40210c9f11006bf4415f3e06e54fae270476bd8020513755c00322c6404a8b4bf2032774dd07b539cc27f76c7b05ed2bb471edea3505c799a42d89c9b14d3462e40030ea31225946b00811bd397beacef5093f483c2341fa0c1a55f4d50855163a2693b9004646caf102a0d24d005bb0f9790ed4b22caaf46f7875552d134524e27ca466305c60b7517c2cdfe04620d1bb43b3e17c6a44f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc060a24fe4bcc05b52ff27ba929eeff1f1e44611813a6ac9e09cd460e509a1f2b04d40997535a1de45db1b0ec36ade5040a3bcd43095d9b595cbad48011c4c0d3145940606574a6154213feb4541bd85f0111bbcc52fdc6896820fc52154c0c9b6b2dd9994ee906ce415da38c3a69b1dd0b71a0b6598bbcf92fd6221c5569fbbf6e6d845e6f72be400710038f28ecc98d31b44cad77a14d6f5a2e906b678b5522727755a7171a59822d390abb34eec9034bdf36bb6b01dc983cc0e4cf49d182c167e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06753ee510d779ba07e6cd6f04cc2b0412047a16297d24ab13f12b6c4701a982008913db3af27449056265226aa59e977af722fd4cd90b190320bf682a08b528310a26253913ae2417594645348249dd31e12d70236fbf4c63d10a196e6422b768984e8b078f6042372e274777122b3c49c5a9716a8aa5c27103ed4c2b47a0bf560c7f3813d888460b00d1e30550a754067c7a575bf174d93808d5ef56ed265317285e410baf68ad296f09c5672094c27de8acab7122bd12409b63ec4333875d16f055122067eed80ee29d7c0c530a9d55885d140a9986fb5743c4ef73538f997a2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06c33a423a9ce0417345051761e89e9f0f4790b2179847d02a33939716f059483886f83328e7347078520e535783cff601f14ad05e48af3c4dbf618f643f24ac0d0f154f01fe845d67976b76549620385e12e27c238220704ea48beb1dc1669b6faf47df14d5b23d055ccb5b43361f677eff8dbf20414ede36ef04bc575bf91d0ff82a6e579e7a8a0a81208d74a8b6461342ee6270d51e5e5ca45dd07cf1d4fb757f066f7d80a46e753d3bbe0d6e7e147862953f3b7aa9bc54630fad6156d28469ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a375e4f6350fd060330d0596fb156be4a393c3b2f43a81657ce451d32c6b8e9121fd6f215c687ae5609ab3a556ff9c12175cf36392e69352dff42d95e8a2d5f54d476525221d8e51dea932d02e67bc9733c77ab3804fc8410826edd0bbfbadf7e012142203dc2812bed084373ba027043d85bff04c42c9b10fcf76778642e5f020c7f3813d888460b00d1e30550a754067c7a575bf174d93808d5ef56ed265317285e410baf68ad296f09c5672094c27de8acab7122bd12409b63ec4333875d16f055122067eed80ee29d7c0c530a9d55885d140a9986fb5743c4ef73538f997a2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06c33a423a9ce0417345051761e89e9f0f4790b2179847d02a33939716f059483886f83328e7347078520e535783cff601f14ad05e48af3c4dbf618f643f24ac0d0f154f01fe845d67976b76549620385e12e27c238220704ea48beb1dc1669b6faf47df14d5b23d055ccb5b43361f677eff8dbf20414ede36ef04bc575bf91d0ff82a6e579e7a8a0a81208d74a8b6461342ee6270d51e5e5ca45dd07cf1d4fb757f066f7d80a46e753d3bbe0d6e7e147862953f3b7aa9bc54630fad6156d28469ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a77d1213d8a6a5e7a6a925820dc239018e97a3f57a66d1e0e0099c04a0688d443eb4d7508eb02ec1475734117a3e60b3a505fc13e3db437555e905f0500cbaa5eba56e8798b17c54aaeaa653cb13602736192f508a65e12584174431204bcf00c4055a9300e3251113e3a781934a6c36a57365b4aea58125b93aae12d402e6e19b362ed4c62d2d55a65087a6938a30e1d6aeb15253400ee60580d87560153123aff63c95fcefc7b6d2fdfcd106548897ca7cfe92a6f35807de0c6825e93612673b217b07d6c88726a6d2b5011ce7ed654bb9fe44eb081bf23d2680014e626b94faf86707d2491252189cf506f84128138c04cab5c7cf07e4b387e2d6716906f0db85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1aaed93e4a563c556c7a34fa664a27e32100a71d18610c556168fbcc767738cd0fbbbc656dae621c649fd22d5f20fd5f2529ad213abd2cf53d4c6708540982340ff32ac77e96d8cb4e525a663a303ebb4f905e6926a3cc36138058a73189f1561049505138a735c2377d4b1c4600f18848311b314d60b4da712ef892663e855c5b9760aa5e230a3b753acd0073aa69ca7a8ee2c04347437345e94eaf751d6fd40dc9daa5269223736c18c9305e60212a093e280028531d5045a19d6b6d16baf638cfba0177ed183e07694c745573fdc4156662a1679b003a4c462c4f4155f4fe606546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0615a3722766e69d15fef98c7b7bc81c294571735e612cba76d447b878c02e7e30e0c9194d60f24c6f62ac9b6ba704516efd635b224f16c2304dd61a73de3fae363e9b3a754754152ad09d1d0800015462c9e1c917dfa75656026b3452f8837d249228e3648b2e7d176592f16f6f4af725c5da13333b71b806256b85758f9098182ce03b16a895c13ed85a1c00a7e8336eac67ec67e5d2876b284ea44cddd3d864e6a04a62c99ad754964222051c1e194a527f2a617ac44151a3995d3ea7acb406f2bae72de34910694c2420197f394701bd69ef56993da716793bcd2c8ba5eb3991d1b04dc92db44fef681c2b96d97c0b58daa573af6739257ab5e04401d12b3d2aad6115f408d972a23adb2e525efb2c309af278c627235dc04a820dc40d4455f667e02e2fe86716895bbd0466726620c3d6340cc221a70a7a57c90c7936557b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a16917f16dc3dfb517bff1261ea8789192c3b1a1cb4b370628a6d7b530efb146c0bfd8906b9adfe3e8c99d343b69ef4531efe021dc2004c0dde1dd2263046554af2383743afae765a4c7ad212855a81152eba4c3bbcc7946e692a0a7b8058be4da4d72969dfb9e95932c0bc087f62920a7d05e1081eaad429c5a122513a4712279ca2ce72a04c9346896df7005e03fa006fe84d48e0694b41efb4540b1c102700f848ea05d299802c5cba0847ba8d7c3756222217f5e979157b74876e1bc8ae5e192f6102d03da03261a34300f52ebf3237090114898b41294d6e2849c0a56277c4eb1759411d4630e65b02617c77660739a0e72d579c865af7a3e731180eff1c26d56327d2cf60535e130b6d7324c1263b76dc2701f20e14e86bf91eeb478d301bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a287c027614d8f5109131965317f0b65aac30e72d1585e272277ddf2d0f226a058809712c44dffe0817800575810dba13090e523096945250c3d4ee60431193368979973fc801de38ff0e9c3528e6ad21a35ea8386930e97b4928030f151c0330b32bc45d03030e3ef66b457c6bc298149dbbb1063276a36dcf7d3b1776b1992191f6a80b86d4095f6fe5bb4fa582251cf8d59e3f2a13b57635952441beb35d7ca3e5b7197d578015a8e73650dc9e8f46a3ac747a52d0cd568e7db43e4d889c0fb304ff0e0fa2040c66b95978f1fc4a23905fd65bcab9624074d8f840e9855d57f09fa9497c10452847d70c60c6b8511e605cf810e4c95b0de6710d629e9faf2962b2e647c8609a7ea25b2d42bbfe845e8e2690799aa4ae26e512d041c1c62f00629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a204ed300dff8076667222f30adc36159ad11380e0ee226526ebab6547d817f0193fe776b6b7d1616645cdf0ff03c8d7894c24b7af15bbf2b98735d4e2986d76a3fa4b91accea351ad185f3667adf59488936993055c1750414068b6c8bc8ad195b3d257ae4afe24379977b3bfd8ddb363c16873a63d3ac6fe25bef198f00425e06e1ac03f40f30569dded62ab3c2011cfbcf743b1216a90a44842b194a59653cb413cf1ea8aa4c02516d5b44ae527d6bcc2165186be4a40fd6f8901977c1bb740dcf321aee2ad87a7bc84d4e9d25f1440ca31d38df66c32d9636f1023dee800eaf86707d2491252189cf506f84128138c04cab5c7cf07e4b387e2d6716906f0db85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1afcfa853d1757e72e943b4f5c800ecd78d4ca564550ba802626725124d6cbd96135f370183cc1d523fba433283c950836a23c4252e8c1b64b7e15a2557618ba132a140a49581a1e18d4ce3143b26b0131e071500a84219100e21a7241737d0414feaeb96afbb6c1075bf1a70a91407c11a1385b6251331459c822904b3d4cc1695bff71441a243450b069ff00a931e2613aeac26454f9ec03a5789851ef3bc53d31579e2ba7647d5b5bfe172547e47130cda4625358947e1486395915040c715f66b6c934a910901323d90e22a332d85e9c8e240390a9460a461ca146a1d3c921ae4e5201b72aeb59277da75408e41a2d0193f82b2b84a41768056b7254b2d04726d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06d9573f0ed70670165513442381566120604f2f46a4c05b16cc53f92cf2f95360594f557e360300299ce6c2019bf9a0230179a32026d74f0de0573e69277dc14ecd3a770f69e6c818b5bdc1120053531d125f6e2e4f3a6b4cad70fd46cfc957676e3ab34f02ef3a06eca1517000e77f56bd5a4b22610e7d4181fd3170a19a955b393c9e52cce1f82fdb4d392f12d9356357f6642853360f1888bfe42fbebd123f6e395a027b01d045e3eb3131b654000993840944d95adf218e687d43f9dca60e22b3db30cd1575481cc8e62a00c6b640ddc5d41c29820d4320721f1b2542db1be0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a082a7c7c09b915039cfe552bf03eaf14d67627040bf9372b2796876fbe4609123bfdc60e00e9a35ab9cf6c214391473c608d9c5676d3782e68620c57fa133348f3c4300ee62dd35ee551bf15134c0d1aa2d2231c7102126bba7bca0867f43e66073eac5f841a61391ac468307a88460e81f1043f7d44047ae2514c291121a2358a13ac31a2561436fbbb500196e1e859b1ed640aa20d6f25b8b40c407df57808768f86356a93fa05d6872d1f7a35807ed4086c681f1adb481c4888517b0e9341f6e8a050572ecf5dee146e6910cc317135cb505a560a5a510972c4326c25240509542d3f4ad01a1f00c9bc5aef928063db21ed1a119b4b38f3db68186faa564b26d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0651fced74c89e3439285cab07d475485ea355347531151329f160141196688120c1ff4064270fe87e90aa2206d095fc710db2754ea549644761871138d170597ddf3846211cac4a1af9f9772c3cb9765ca18b283c31fca338e1a6b453c1fab12eb0d08c1466c77d4d2ab671099f9b69430afc406f6b501c558f50184c2752c13428100e31008903588661a647c0d78d0f715c5f1517381f7744fc5b1d765dbc4e7755a7171a59822d390abb34eec9034bdf36bb6b01dc983cc0e4cf49d182c167e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc061dde35569eda6d39c1136123406c053294c54b077ed59417357a89028730ee29c0728457cd63f56271d07c2f79f72e60d1ec87736a489e21329460130301d054a13a754a6ac940396f6215417233c77ccac77120a574e74fbec10568361b7740e99da74e5b3da65827255d3e004b1636be78666c6006515df25d3c4a491ccc235ff37c74c641d355f12fb82d35f5bc410acce70bc3192b1d900ec335897d9436b8d2811e6147b060e1646d1794fcdc21bbf9115e89ed8856d661b06856b1ba71b320910866dfa775c43470214367aa3228bc9a566c8e9e096a4ae81fd632a61fef0f8d1851c5770e9737b061d5f672453e6e4450ab4e66624e2e1b0a2f819a19f04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc062994d9795dd2c1178eb394614b219705c655001b9d554634c8a59f36e68f9545ec21a6624c5c5757ca01b4749b4713714fc8dc4a43a8c54dc521bf62ef22255522b90f55658a2a07465b6e3272b1ed592d1b60621371d504f741076f809b105ff8f31343a69f140d5a780536505291573b7672107e6c5979af9a3b09343d5563d202785f5a294b2227b5185c70ebbf791cc54a651299b2614f32d1643f73f3282314b74395a9cc130fe8e26482fe3230fe071114f7bd351de313275ed6bbf93b67b1d3158af9ad715f6f88703e7f1a1b83c28939d0f31a158ebbcf18d8c7f51b98ce1818f6465a004714b750020b531c21f0266ff545c551be8ddf6e867da7505962da2d6a0e2f1f2de27026d16ab47a5d000f4493df0a04d0444c0bae1ee84af467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a413b547ad5d7ec1b47e261272e040c2e652bf4027e6701251167055782c7562b43465f0db7baf44fdfa0793a1759dd2888b8cd6f904d66769443a1323b3779771edb8d4f67da9831f8f9ae795bd8504def7c301c3b75725cddcad545f7511171f623a66b2c55e121b8ee834645820864db32e24d182aa203af58f82c2e73b54427766d51e2c2cb7c3259170a6dc0014414f9c2197ab8e541763bc24c2164613270d64c29ca393b590fd38c36598b8a4630498f2a9727f3708e780f63e607be2c39eb4b4ba62a565f43e7c800ef4af2616bd57c04f400412dd5213967a52645063e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06fb4d4e57806d6f551f0ada7ae076c745fb614b21fcef35247e91554b2a6ccb706156310c40cdd464dd78ba07ea5a6b1d90d06138ebd6cb48240d79398f24f6532fdc41401e1de13e37c0c21e5b9d5220f46ba441b5d74f19b0588c26c8b5ac1d0b94851e95ebd23a3387ab12c1d72e5f736b78287c459e5760770037541d91316f475e2b2bc83a7371e3f75c15668e39d2106a7026698d2fc138a703110deb6e311e9f70400f3a422889904f000e6b5fb39ed672bbbecd2c5ab0e4237d05cc17bff400499660cd54c769d04873125c758d521a19b79e1300f70c4644f600f842e5bb1756d7850f780dc75e41aaf8a82569676c52b25b7a102a6f334025d35a71bdbc02148df1791ace83fe277a36356c6571743834959d256e9ad1380bf1e5318358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06ce5d0c6371a1231de126bd26d591e3291a182073c10f83483b18be7afd77fa3e1fd62e0f46fa174f9360112c6d1acc3e9dcb4157b200de152462c55c5581d72a53567d4531e50d08bac6c7769a59d0455725ef1fd90f4e2daa9d4e0236e33b7a9dc6e4417478b4615c9d5e7eddd6067591475150803c1422bb8d092ee5eaaa5f70dc8d0512487e088319ba69ba40bb30db842f278834981c4fdb8b6d6846216b2fcd63619549613aa0576214d8767e2acc902461a7f6f472e4de865ae5ef3440e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc067f95ed2e9279523c0f7a8d7b94ec0712cd0c5369e2a55d02e7388d7d68f42f61a957b908c8491f74f7dcb735de728d3345d42917d1acc14744932027e197574b44874f2121498869189f4945bd3dbb7c37576c72762f31529577d0446031357cebb3e26989f19e3239df1b48e0da947df7241c1352373e2a7db1d44a68639a615f046b5f63c2316b39997b05f4a3141e4d88d132eb0a8a590ed63b452e34c84c1d77ee6030d64a2910503e56872fdf609cdb877799c03441b00f656a718ca860fb9ce34f090e0f65e8fa9028deceaa1b98d15a2daef5294c96a75b423970066c4fdca6594844e31048ba91122597ce5fe58169208a2a8144296fa45806f2373426d56327d2cf60535e130b6d7324c1263b76dc2701f20e14e86bf91eeb478d301bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a11e4e849c1890f079e3ce635821d421417f0323c501b196bddd80428d0c0826883b83a0df26e45280e273f3e91d7984d316e055bf0d313767a6f347c86bfd17566c59d4ebc38324e6845e62ec08caa3214ea566643569b469a215900c17ad812a287840d0e19bf4e90ebd8419e22a76203c9103d3ab29420ec1a1833c07dcf16942a852296db976a634a4678fb62e42fe81eba6eb3da373220bb2b62ce868c24d0de272c5504993463eca01d9f81ee0d2e0c1860ed8c9c1a21c78232c7a5687ba26c6235ce60191df7214e2c1352285c3aa3b033e889af62c807870a08ce85656546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc064fc3ff16d031a96c25195f5eb6fa8d18d0b2ed361687de468e59105843f5b476aa386b238a6ce86cf242164af7fbc4625141a622413ea84370c7fb57057b8d073caf241ed946d83e1830a663283c3400228b6232e72fac112134287933791a74638d441044da430f8dd3de18a467e3291648042c3012f37797589b504e1c3e3bfad8b45c54b5b66dfde8bd199452ee64194dfe4f5047b0522dd29f6d543ce7364c20e00e12a5b5039b2911701f75226cf56d16624c3f4f7766a1122fe9f01230e7107d39cacd150674e6ec4c007ec26ddb16341ad35f5f7be6cbb22fdd47d206928272200e2742713a4e6d05985f707a46e22714a210d96e21c1f65decf49719a496fb66b9604a0a73c863619930f116be73497136eede1854a72b48ee3dbc18d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06c343590e195cbd232658ee36e2b74c1db1c4d87d70bdc54a1eb7392f6150894808ad6c28d7997f457f4de258e7de8d421f00566d5e9918655f98822b30b0d877af31465f4d0bb441591796596957d555bf263608b340504b9661642e3974d733550e9d6dde254e31eb1a2119be7d89137e0c907db623050a35b7b1643e664025da68d25a920d5a1974ac861cbcb5cf7bfcd63563be4d167a5d8aa14e880be86e735b7c0bfa098a05ab85cd5f8fe965177f1836075cd0c108a198a6021a215634efeb1d5a98a11c762b0ae962cf72cf73d8d9132d56e02117defda23f6eb0321b9a86747c5602070020a605629beee921b24ee52947e9245a81a03f0f4522b05ee1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a19f1186f27d03410841e2853f4023c1bc0627221bb45a35487b69f31e866cb2c2b23433fe5f9357776f836491bb1022b6b4af848b980cb30549e290488c6ff68aed17d30164bc83a6e35061493277347d37e0a5e24f26a58b66df22ee2f22e01a051d618c004765b6d9529277ad3fa6f2458016ee7cf1272205f613254981d65a207be1cf29f1e685f36d1240b71bb6100191e5201c3126b535e5c070f7cd43d4bcee17c347d0b55ca2876313c7358363c8a005d8d3dd52748748823c009c778441ced0cff92fc63c4abc82753ab34158b5484766522da4a14014d6ad365963bd4659e568de9d42a5e4dc852f728567c9c34bc4b487d6f6d63c5b5651f79760fe1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a32a4d5617c5b0a4d8432b6253130942405d3e11a4e48235d7fa4ca10fa17e01db4527b265b48e16a94b35d1735701850ea0bc46b9c3dce3535817271b08b2c52d9482540db8d8a185b4d69387d38061213f393333fe42f59f526cc593536fe4f6ac5d162fd76f355c4195a74217b864a404af57dff1d41104b50f433023ecf58a207be1cf29f1e685f36d1240b71bb6100191e5201c3126b535e5c070f7cd43d4bcee17c347d0b55ca2876313c7358363c8a005d8d3dd52748748823c009c778441ced0cff92fc63c4abc82753ab34158b5484766522da4a14014d6ad365963bd4659e568de9d42a5e4dc852f728567c9c34bc4b487d6f6d63c5b5651f79760fe1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a2d41b667c6ff436840c2e01c4e85d8295befd11f5a4da85e58fc2a144198d17e8b799b7593fe7a5f08c6b5022a23684411307b4eaa3bc13411a96a5f8438c763592703206fcc512d6ce2383ddc8b621cd5d8210aad483f4a38534e686c3f451b3a9b0701fb24ba010d0b877aa867d47046f99d573885e051c291911ceec1a706cc30e655e65a83396507976596163503cf54990d005daf37bf5ced611f517c79032fbf40709746741df7a863b6c6431ff741321685fcc43966bde17ad4acb73c34f3d17259c12f330646590df72ceb7685eefc270cb9ef2da32c3538581aaa131cab0c030d6b71384ff8013ef74fdd63c215a20fa55e0d30a22b611c9a1c0550d0c2d168f33afa45998c821e6ff0bb3d296b8670275bd738ff26190bde63f0518392e05e05098074a3c05b324ae9b203adc91d7e3b59a62826f835254c012841024e6234121c1e46e174c132e4b56346ca7f21561327e154da4e110f7cd2606d091b083ade19362e9f724452b520e8481cab19103493f550451c4e361ef121413506ed0665686d0079ed21664427dc7064f3472215961c30aa75556601175f3327fab813306dfa2ca7acb60e23aa7a20478204698cc10a7569abe239f4076e2c3486b311fe6f3f1b5885247367b54b6c75179f7d5b17cf6ca365ca2f82962d61966ee6354c11682adef37063867587447e01c0312ca52467ff74607ce4b90334df81e05c71debc0a9ff19a4fda94af2698460960008d141abc412026e1206f552a0e956fb980996ef4bac901ea1eb34c9cc6273ec8c2de4f8dd4f92b2bb23c325db4ce0d10453234b3cbb04eb36f5d41a916411736d29b4cd1232a208afb087e976af908c1895224ee18614887e6cb490a45ba6799111e4344b2ce3aa70e410c8638cf62208c42225e74f9384f88ef0e887ddd1a14f0e877715ab61f3f293e339cf7ff79971fe15f13d6d63598d00008e583f626d4aaeb29e03cdd3731cdaf55b62a897420e06a2ae7ed714059d75609c442ab6cd9b0b915da3adb44636fd0328521985197dc6a316c8691357f651a226697d7560b4f76116516ae50a90f66676599a15522b5754c85f3b97b8594ed51c422a57ea051a6520749cd42a55ddf03086cfa65d85e3124ebe363071f3e80226d365c68b97eb327e44d0878e62cae37471279523779516531f06709f5627a20c39c6c4c49f96c235a998d42649a7f13af041c3909e6141f91508b4df31f412dd0eaff1fe927fe3edb95f32b2503067d7c777e307c65a428b67155167420bb650495e2238eef2062d3ae657824c72f3003eab02b4ab8f2228b7e7c0c80890e491cc80e0d0e13d163d898e061b0339f6594142056c4a8302d81d45c0259f2660e89598f166d9e6820d5b1ab38a39bb732c8d9cf289075ca7b9ddc4c15d196e75dcdcfd9113b8db851a5391d63daa1284f6883622cdfb1cc702017ba4361453d32511d685a816a26277ee8142467e55e140da08a762fa89e007ce73001a401b068f14c24583160a47560f247296391d038f48ff45ebf2f4704c0e50350f6c82d5bc16df30cb21dad1e58d9ee0d7603c84102fae31810696934296ed84b29a5186916ca7d5ee9328b07666255788b7af64e2d8a235628587a5a0131493ed5c9610d349940232ea04710faaecb3b5cfeb11537a9fc52f94fef64ef6da568059acf143d201828a386a72fb07ce643a0ef535fa7e9e10898a0700670f480690da44e5deb75c57e54202278548a2b4c64f24331946c7561da116d3d03e2352d58614d13062a402ae83dbb42e221fe64b1c6631657ae6f36c9fbba2a22381957b7d1ee6cb6d77637d97f8d5aa53d1b1938ae9910334c336b0db7c1231103224393b9c72a5da5df004ff9331a7946280f9aaa021f32c5ef36b4cd3075b8d7ca6c6d920e4fded3d81b1b0a2420b058b245f8cdeb0b07d0cf54bfd62e5b34f83f7a0d032e564e749f0b29e3a54ba08888585747715da822ce35172efb108fbf097aad5dcd2204be2161b6953e53251845163122122eed924604544bff789bcf0b373872e02ba40d97621dad31029195d35461548f4b2587637d2a43ed1f3401505405a8f0046ec0385ad02540626b385053756b473241c3b6599a2abf6aefe3114cf294427bc5269f286ec1fc16e04f3c38666eb63cc078f2306d6d07467e96a31e0014ea768e956f352b192e56b8a167394192a0200636d560e7e6e537501bfd4d21b2d84422353c2c10477d5e5e54b9310b4b3f0d824a9927487f355670f04f25e5a3880e2885757323aa1418e736cd71e7847c0d44847f3bdf44c63da940f009c235f613533dae4beaa92a094c9a5766847f6e433996c9313786b86f4e76d05e25e883462c9736725d96df54ec7dab540c746d246b9f2f4cf37aed17f807375e63a2b73634c8e5147622f31cfc38a51160daaa714b40cc7d88df2148a2760f742b610a0b44c2446063bee87b117158772fce524f86a2f153c32e3447d96e482891703c32396e3504d2e1780475239a22429f1614c108430d9f47320635165560f501db19d5c90a088c7f977d68ad8d556434de51ce010f4207bc3b67f8512e738272ce1ef062584852b0f241c41d616b9d6158184714156a12c1b75595a1243c2044c57797d936694f424950bf85aa6ce8ba443da301010af6a3254030d45a3cdebd805a0a2d0b59bcc75b032c53dd4ee39595246e02cc0f6da7c6256d09811d924f4b41f146116adc95d03ed2b2801c9f2c866fd465b14bbc30961990edda58c02f930e713c1b2bc46a561147c5c766ff310d0c894e375c6ab9af392fee4b28945492712175e34222adc56dd2bdbe2a455c4c5ed518dd2ce627695aedb7d767da8f183fabfa0402a7b3510c34da0a1da5f5aa60a899781090b79720f5df107264c258588b60340a85a9e53772bde66e940c224a2b037875bf565e1e9f07bb335d429f4295339220c75562580c1bff3656702b27fdf4bc0e964d616a0b3e7f1a0f3d507a77d707170fa0fc3dd02738136b0c0a108818422a13879a15413c243ea922a16de03164712e964550e1fb98398720c95fa45b2a438b04df4020581d5029d1db494d5d19731451a1471c3b4157631afa297f3b8808f25f8200c2340003767891530f916948e7ac2b22ac51d07d3eae740e4cf916401a8b352c6627fe078915b444ee1c330f8fd2096fdaf2a146fda7d36b1f0389570721d56b4b968c64cc6cd56240d1f417bd6dc9292994703dd233a55d2d2af747317bd619eb67f84fc3101815bd6fbc5c0fb3a950d6f92c3ec3b0e714a688d93fa553c976c4f8466ad12d8e35173bd12df6f1b71c1dc6cc348c783c42bdda0473bc6b051191d22a17f3a2f90637f39177029e86307f8aad4343cfa171ea16b828ff9ef11b4074b43910dca064463731153bbd8e4a07d7fa44d44a031c2e7cc82bc74c0441576ffa020f95440b7025f22fd0f4b07703dead7c5010fb6183661173a1b276375878f43893883b66d39a9d5db0d7d86fd939e0300fbee72559517c2c08117b0b4c48e657e4fb792ed7898339f294163e33ac0029ff94fa4c392df4661a020004cc0d8a6f214e075d7ecfcc3c3c829d3c99337453e07f2e2c82d02d2f7bb6372975780e5f39aaed30bbaa5f32d103da53473977115afd616d210d254e2b2e5f67236bfe184fe529071ca8be5623478b265da6e6055a212a1aaff1cd607ee2a76f745ab53efbbab071814d637dab6131408b5d845f04fbaa7e0049d74b479dce3ea2aec46100b12e1b28b9654ec2160e6276255c169fb78962830a341a3792c843c5c5e643effd151a9f271871a6a059491eafb811827ddd5bd169d344a3477810e5ba6a1e5afe7c1ded2ee93726b216065a9cd16931d50c43cb442c3d9864df41c8eb8344cde17f3a19fba745dbc8670acc547e3a153c6200251ff45e920ff577966a6f3fc334c91eb26d4b6a3dc517063b260b7e06c0e736c171746c3b26bc7200aba959736b927ae3aaf47d816bc312871ebe2e3b7c1c3af3fda408d05f755f67183d49c913fb00037afc605179147c81d350197f0d2a5fea77d209bf5ff22a7502f7447a0e7122840c5e2acd4f8301c66690369c2ac6197e5b2f0ffb225f7a9cc1b61024be610adc15400d2a758a0ab98b7962e73f1b7c433355080292306e7fc9fc4c2f5dfd6d44b8430d05e8827b9949421c9ff68519f9765834be7dfc660be9984ba7dc0767b481a627b682b02a1de2bf3537858a2f15742663221f990b3e08f81b5b21780ea670e6741e2a8d509facec2efe91d11acad11544cb517867511bea6dfd9a6712bdf1932baf19ae7758a5353cc9149254652a816e15177b7c1bc5cb53e140116c0788e10857900c2c622eb61a67b48513758a9125a915e76506d26b0545929c18415176554517691e1da9bc540c81a437c111202c65b51c1085a4d54d56e9e927cb9d083182a4d85cc99f714fa7a7237be4bef9101296e170a76ff41f8b68af3af8f7e7447c5fe3191997f104c1124379d2ad147e5ec0e679b2e2c507d5f2224d31918554c019d14765813d6381f10f48fbfa4c4273980e4cdb936b6a1251d217414bfe1f3b7a9b70082a9419964385179ebff678a314377db58b7706e5ca0d7a02f33d0a3074b90ee4d0a22b1e4c3023643adb0c150dd12727e73876ef0b3c47531d9e4afe50c569e22d810e87386268d0ef3c2c511ec56c8146ff2e0c662415d122c14f9877645a728c5d1fa5b7515b79f433288d5b1251c452b223e2dc1b569f83b24183f6b04c3280bf5664dfd96ce50db907bb290b5560c51e05a93ac447803e8f74cbd3310872ae3c34acae860b973b501a387c676e2a80fc1a1682a41d15a23a6176c7e9762170ce2f36becb36ada0b964adc8cf50696c833dde795b74a1de322fb7edc0366605f03b267f5c041f9fc53d6df7633cb6aceb5c77d83175b1714a37de977210269efd09b1af7a65b639e35b98936b3a1cf1455965a2b70e2dc5636b40fbe603276a91380576e7721ae6fe3b8bfff052b676af57a19e51399e3b120af1e15433a68fdc25bb97921bdbcc4f612f217b1cd01d2a63d05efb429a045227e51754462cf68d1553c66840f2c59549e318072eb457ca5414273d0f5333037608bb5f4ef6a338510b2c6a6d45970531b1a3536ecba612570571071167b45c205e62820e2ab7cc1c15179a147f54292c90ed811c810fb955ffec45188a21e55177b9f8455af61232711cd97b0a0b2729a8853412a69325619600b41f714b0276fd235e2ac99c841de431f626e801ce360722427d576b872ca252933c17400f6a314add2b09eaa04f53649826b788024f553db308bdb5da0ae1bd791fb2de1303db52765b20630f0847409d3af9c8403253f09a6666e426068594430cee62d35185d5d71fb52c8c3570af1e6d5af8b950c8a8414c187ef936f4a58f377d2ac56c7dd7a367c433a6063132422685571275df547c00e3d68412d67f4e678c4d3a14a346842c4ac1736d7cdc446334827f0d5b060649097bf05641ff756144f5871b6a6ed624b9d9807b00d0cd05d1525811e8bcfd64cba79b368fd4cc296e672770f7cb72083aa22525ef66c352cefad0008e699f6c6d6a477345df4a2f78b0724a366aa03871be4727a0917e1367f3420fe57f7f6ef628f00c94ab114cd9cc204b58d8ba47f3bc40219f6a34570c3b2222f6ea0425903dd00e587e7f2f8497ea544a4ab725137f536712a3cc59a8c22d795bc23f0e7beab42ab0fdf62629a2931fa477a52192bad4770b0810038241110cc22f5e6c95452c70fccaed7996939544af1af805ce4f972f0617575d9e6df34cef2b9c28fd38135d8ea903398e80e60e715bbd34a70734096998201fbd11895a48446c2be4a9dd4d15c1b2493a78ab00a4536a517def785161ebf440e7854a6df2ec4e32af6a9e3619557a664d607568a25e7d40c30d887947629f2c98d33059d8dedf490ec06c00835483127935fb0a15a49d226d1fbf3f861fdd33855d333c6ee6e21f7aea7e236714a41cef43a77be1b5746bd503d63af881017c702eb90d83ccde5bde1d53341a2b8f64ae888c5558276419759fdb6cb29eb871be620e658a9ce91594ad1d530fa9c46dd9ca0a583a2c8550aeca026468f6886386d977425bff2d16dd06c90e4909a749b3cc391790f91c5c2cf1ca381be5614f29e9fb14b2778a0e115e657e3c7540393a56285c4ad26a495dd345778705151727b9981e95191b71ae450c0f1bfb09799dba3d47860be50cc9f9383763b33557ece7ff54fb99df4569f4730b50730812ed50d616ffe7892823099c02a89a561229cdb77cb758e824ca64787832480704e78cbc2c9c1690749b019f18a64f446cf0fa27534bddb94b7dc4e925fb18f10104740a713e4d383f5b3cbc17cdebe2713c5f453fbd963223fdecdc086b48ae1b5eb001317c17860713e7bb21130c7c4c234a9244eaadfe4dca0dcd7ba3f3a150c7a8872e5a40271600aaa12356f90a28a3ec8d0d96a7a9654cdc31479ff15179b1e6330e7ff1502b3310707c36ad7046a093e249a565530112079b26dc84440297340e737299e31b050e416a97b2d260b15cef13487a646d331cdd28b66cf01004a5a56f9a400e132a2e73333674a440f83eb465c1610b7807676119ecdf074ab24b09224363f152affd2454bc5ce43c5f3c1f609bdba6620911d26bb2634b2c3932c84059df9848bac7ac1bedf6b64ca1be6122fbd16a3653088f55ddb5d6441e4c0952ce2f2d4555733c4e357ee11701cd89176c75a902219cef319c76a43efb3c1570101d664d48b1963f96d398366068a617cdce925446509c272a966b7855cb962890226658c7a16d131af56e067a4bfb04e3a3253ad14c911968f6b23b0fb92d638a1a661bc6e9964356de8b67552eb235955f376f5f61da07bdbac935f0906e7286786059e5c98a5d0099f940ef464f2a7b38b551d68133544a08cc77e0ae5a2466fda65a377bc7381e507043874694439c689f332e365708a7c5c6221a08461ba96d1315189ce759161ac66b00e4ab563108642312f440788bf24223053a742ba77759073b067253c976cd43f8cc264cf0e0fd3b3f78607d827b1f21650086085ac1571cc8d50b6145966c722e43da235e372655af2f864e1251732c16017b5088c55e1ddb870606e5176619a1f05b4a8e149b5d0158b72854ef51727f6e6c6e9039e864c08ba04d15b2171880269621ed481f070b938e48121a711e653b05607981c46e6fc39e49f197b263f6a4cc076d2e7423d540524d3b5bb675f2dc6f638805441d309f4735af92db4f04b64f26b062536923cf0a534d86a14290a30730c140eb668edacb257c569c48e88984382585f32d40a215125e1a337371e77a2fa5eb3b222fc39c574ad18b1437b0826a6d3c901a8e3cf66215982466e8944926440b043acbe89739da87421cc58e856125e23953f2e718445e1b7a039952a76689c3f6143dcc1c371fccdb5072b66b13060b2641bcb37e293cdbf00b2d5ebb4819a700779cea281f98f5a6185c71111ab55d1047ae9af90e893eb0582a785d710983b46085c3285641f754015cd5026a63845d71e9805a5259f2521b4653322aabf92912a21a194829d623271097284ef37a333098fed818be58ca40050b1f4224459001ce593954506b5b3581e0f659295b9f0ca4b87d02e03ac71f357d253d7ea48a11e625c100bed003748bd98372be263f7c9c2b5238f6f57e6b8b570e4137c0173895f7784176b67b468e7730422a99ea15a44835756ef0eb2ea4252c486bbceb7931b0cc7d21df9d366656fa30e2e08916c3d22755c62ebb0b52a3620fb2000462f159d2254f277152c5d16a13433890228ecd55405807240a9cafa969167cbb228a93060dc4cda32f79cd6f2aec22ba2a5af3fc6be4a9f03d2dc2b9292a7e8e2d307c311602a30a632aace364f1780d334bb82d385f8c9f3aca1bb21fdc8fda1185de2b251e07af79d6bace6ea015973d42ae345d93b00c268913c7291cd96a728034062a7c558f2bb80c4c1780532810cea710723f458a38bdfcbf24c7eae4311d4b3344cc39e07b425ea000bcd242239bcee96f39e7235f32181d4bdf80fc46c71ecb59e83fe15dfb6d21190cecab31cf2075239c1e1e73ee5a8362a3d33e084416c569ce33983a9128a05092d22f2ad591186ed981f3059a5ee546b69cc92c4d17fc7dba3947571b88f84393a4077bc735f952eb550756a01c4754c923a731cde0242737f9a83cf1627d7708c2c948bc7bc36f102c20066718337b2ff0a418cf10c60968140e636e33365c995d4e3693da3506ff4ae440554ac12bca869172ee2e073e3bc83a52e0795018b4ecf04b9cfdd778a4686c591ee8d3687c0037683ce3601e893aa86af4c3b036bc4bdd54ee09c850c4c0851413d8595e40fd0d44224bb9593b08762efc78351262e371719edfe0629ff86e528bb7b4032c135705dceb87083c250352b0df71580fc1731467c01771385bf637b7c25876cc3a305c3f11bb518be3e324c3242f08f7ddad4242fb900c7d36137ae35f486fab46401b40031644788f8642e492fc4d4f219b0fb857b702929682135105561baae43b4e71d619123884f95a135acb1e26dc950f93faf30f78f5ca5f2410b9266849a84b2390b7063a67ce09157413161a702138f7ef2f36df70ca4b35926f2fcf6e605170cd6e156813947c60eb0b298d7cbd4458216e0c1e631745c0625756e7b6163ee5020347f0d51a7c5eb27517fd38f544b5e1ce6ad44d4a76418ac65ac18f390cb63a29417e53db73a6de9e6d1a974272d7455646187a68072a38c22667495a04e38b2157d7fb970f1b3ca91deb7ee50ed9bce33dac77e343c0e589325de074088e15b87bbb64b23dc4ddbc2ea3e0c41045f5c2348ea7fb53e208313eff776b21ca8ace71eec36475577c1f0c37b5df78bb231d561e6fdb57b455d845a286f05aea0a2825d2cb3466ca627c34ba23cc308e17841700b27a2109401c668d2dfb09ee2eb9468f61bb2212f9aa1081003b1c1c10ad7eb0e1864b42ae64370853e66dcd702f51c5c8970ae459c173c4783433ab79353a08a2f8685ef2831c8f068d1cca80c17044c74a6d27370e0f0b6a7a27bf40fa5c1ea5554db2869f02d6e13c230e9094735b6fcb25b666b4374bed894701f45d3be8acf23dcb217b7222f1c12de3102d4e09dec37e10992c2a5b1fa451a67b685796bf7c3292f0010f26c92553cb331c2dfd4c7f62dd4b5e3da7e1c467efe2f72461519f3d2898b8481dcf5f7848098c2418fdd06e19f5561e9d53675070db713aada7d2182bd813499103af7d522620725334192afeb6714cec911e0e57c9644a771d190a5071a8476db77223b0766a2adbed2c46d6890761cf71cf0cc381421fe318cc2ea55c0e390c9e2272e3f68128e812293177a13e50ecbd4e333391c95a917d7c4f08fc6657aae96670ca90631dcc75a16932a8c039ad561d306d18b214957f5a4a724aa26828c04e37b976487d6de6b7273d3f0c74931ad277b15a425c959ed46367a09f3250d58e1db7e68b01e367d367bc106b55e80646705cebe9637d8b66485088b92a45ee6c51ac00a065f6e1a319ac0d09403282824b4cd37b697edf0d05a01c77200bd66b4c8b5b66474f5fde2c3ce598525b3fd32bb911e477b522090c626687048e2a14008e00f53ea10fe3409ad2ba11256f8d778465e738767d11636f27ca6b50de3f3329071d3a4ac6fb2c18712667b71dad57c0b9214e79d8c11d4eece20b6741e747f2f8096b92e55f0fc7508b0b8ca11657c1e99443ca3083218ac5420c0287f35464e29f4787e8d4788b501472a5ea121a0e56435161f6187cbe155f57a46df835f6aeb43fd348d54729adea7dc15bba522f90357a736b5406cc9ee94ab7b3207ea80ddf69e9dbfa11e80ec02a4a799438be7e236c7f1d5f4668219c0dd9f34937616b2e78cc5c7f2f0f4ec641fbac702850ab912100676f476680805eefa22e7dc314556e7c314a775014ed6575e7a75ca42fc64a90e6e65bd0869b367bdac754deeb7f27fac85c757fe14e7560f3c82dc26b917443a86753662b880553f7ec5d3d121a3520a6c261f714a26075c23d154145294a18344e2853cc315971625f218901fe6b94cf4c5030c3f65389fa2f2fd5c9210cd7a73e038f7ad957f9e86f5d77ad3a740cacd53353a7f5507163756a11e3cc60b3ee88592e890f5b8e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e17a9c14864912c6148c2e213a9f8343f9e29c6495bfb6b65438c2e69c0c9e9388e70c43367a58715696dc05bbeb4ec03b1d69506c6572c0f84b5f812d8d4721a7f713167b5236248adb6c332dbf0b91de9cbde77f156281e1fc53e4195754e09f544d55e3c0d914daf40f6579f4ad65ff099fb3a5fbd452f280a494d89d7c16fd4cd9870ebd2c475e4b16d2cb1617f1f0f5c23334939b17efa6f23373331ea0407e51b70d0950507cb04eb64ede2e310a105457d33e3004f3b0a3a4b6e7b7a11a682c3728eb1b841a4885d19853651657cacf95779957b3b74809967f2e925788da65c179cb0991149a40974b816725f1a3fac4e1a0d202c5dc6bf47ee813360480afb1a9327de300961bf17f59ac955100e770ef504e867d22972217b17a4418e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e6173ae22addd6556b9a12b070bfd7c51d538e906c3f33d21078a183e57e9f40b211a6c50efc0233a7c58ea47327d4f78003f9c421cc77c470bd6042f40d9a1301eb79351d3336739d9c464156b939614e117066353a9d71b1f9c8377b213135a57e65014c42a0e1a6d5f40782a0c896ad19d0d17480d54626d9ad23ce9fbf1483604de23befbfb219826bb4ccd08c85d20b0db7ec8faf4166f4f8a249779ab5ac9fe903422a5bf152500527524c3c9145638da5d45afbc14ca22cf7973eef632fe02c64900512e4a8ce1896348a1b479dbc4422574fcce5ebb7f093b8de536072588447c81ff78325410f23f53c5061de1e2584e4b1ca72e17c8be696b255d5cf30e6f6ff455bc2fd70bab00f43ca701a9a3ee1ddc0cf42f6d3af176fced481e78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a5fe8ce082041231c35d55364d4f3bf0834a5e31d2f2d973de539bd48da84c81c2d5290163d410653e63414310a4e6b15e83259361add1b537f6f6c3267bc444efc7ff5423aa4021649f38f61653f9a5c20bcc129e49568157b4c0a2f52234f6b2e64624ced676c7c685d8700db64f22facfd363dab704947735f096de78fea23fcb3d13bc824b96b13bba0553939313137ee2c4d17079036dc7f477430286d79ea878069afb7a23522265a277791fe743494102d7e1632177a16552ea7ee9378fe02c64900512e4a8ce1896348a1b479dbc4422574fcce5ebb7f093b8de536072588447c81ff78325410f23f53c5061de1e2584e4b1ca72e17c8be696b255d5cf30e6f6ff455bc2fd70bab00f43ca701a9a3ee1ddc0cf42f6d3af176fced481e78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a05e9fb7efb6afa56e8f8f1174d9972370e792f7d647fa73bfd1170166b059f19f6477b08d0ddda20249861425ac6eb1e618379372d08e13c8d76e00bd28abd45861f6d68ce59a11e078e8456b400243acad8c524bcc9542240b85b755831e05b676ed2311060825849d0e82c978a666c82fec66e7f1eae7c7603d05a23ca240985d2005f0caa583295aaa532b4b94a3f4eb3393f702ce6230bf7883790492b6d8c8b5312bbe48b79194a8d178cd62a6e4607c51f77e19b51efe92e7397a2df09ecbfb61868db5a682bb07564a4c988644d6f0110f530096c07758551311fb60ec3cb9b6cf8502c1f5f09701acb8729669abb09159dc8757504a71c6df7a1f61dc6de225be0251e349872372a71771c141028c61f2083546aeae9b453cb0d6a797b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a06ea49434eeb5957c5a8d51e4a5aa24979206517e5553b5e7d2f69775871184a4856484f289da6767d5dc028bca61c1b1a266867ff3dc145b231b100629abc3a1839755a241a8d17c03ee87650e9b133870abf74e54f221850fd6e77473fe035da00033cd8c00d29ea233c6559150d53a195b16099a9d2257a7f1619a1109a1528073a11749fc00c6c940303ac6e5c70cd2bcd74a498cd14eb146f0f0a8eba2adf7f233e9c28ed2c0690724bb0a98f26e51fef0de9748e353f9eb81138c8d14c95d36d0c0effd27b5602b627c58e3077d3b25e2dd7f72d288dd22b1898869007c125a155e43d6472f4556d1f98fee81dd07a0c0826795e5b505ca63e6abd6804cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e990e9d1b395c461c648fb32750c3932752e64369f387487135c8f761bf477868d10eb73bfdf40a0e03f60c12c5f8447cfab0b72ed844a879b077832e2ff16b77ef7a3e161b44f556de240a0b5acadd3502c84a6559bacc5a8591f352747cd7650d14ee2e723de6460b8af47367719763d1ec4772b523e5203458625aa20d136a0a4afd5d9587995322d3cf72f4669279fe787a1fc17baa39df69da46732d3a7cd14255684f9ca35c86b037649e9f6065c23763615d5f49371faa521a73324c01cb7e636d854a65426415be34ec420a62e8eba0128e4e83307fe14963978aac02f536bf104f51fb62a7b61f4524d44a202b4baf087d2f8278d4be6a4eb5672535deaedf5fb052dc43b8fec03d00b74a047edf2a2ba0acf00761882e098e16d558ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e15879241eb4aca449669864ae1cd6c5c79328b454a9e7968426e0e12e6a1573abef8e30a05507c070aecf250852eab2d23ed1b66d3821d0de265d76e30ea167badaa4302c383ad4b7314eb2305bec8094e76d84c8f632932c159c8154bea993422bc5076432d7d5e9563413c282d5a0f1a4ca01fbdd8d55b7cd7ef1bccf4a01c15fb6a37ff74b846f40f5f6bafa7f95c960085094086b050841dfd396a1c1c5cd65d6c14f90684142c5a9b6b8ebda445becf722e1714724d71c39c1f04b5da353649157dd759c71b3435513e3fac653e28c5ed4153d6b825a12e402f979b6422c3cb9b6cf8502c1f5f09701acb8729669abb09159dc8757504a71c6df7a1f61dc6de225be0251e349872372a71771c141028c61f2083546aeae9b453cb0d6a797b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a0e41665c4778d116f4040a62872b06775e7efa51a8bc75494b63af6c87cfd463139aa6198a72a076a063090cc063b074adf6e31e5dad417e62fb662d8f122e15a552793725075b1d0cb88c37d540a549ead51036e4c5cb2eae8a2900d2de5c038575b62eae92925b0ce17a2a649c651a8822c733fd4e2c1a72f709365f0f8c21fcb3d13bc824b96b13bba0553939313137ee2c4d17079036dc7f477430286d79ea878069afb7a23522265a277791fe743494102d7e1632177a16552ea7ee9378fe02c64900512e4a8ce1896348a1b479dbc4422574fcce5ebb7f093b8de536072588447c81ff78325410f23f53c5061de1e2584e4b1ca72e17c8be696b255d5cf30e6f6ff455bc2fd70bab00f43ca701a9a3ee1ddc0cf42f6d3af176fced481e78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a8fdbce1de2f79f35223ba11271191e41853e4e35d34dae027523b639186bdd534bb14e6fd26fcb2aec381b792d9f9b0d2191b332be7437701ee51768cd2ef364e1d7d262cdc9d4023820b2706a1d902b41488c799cee1311b2415138525a2e2c83187527978d631273e38427b5f2170d510a5e111e11d51bb348fa1d8e6eeb770c5e1c36c21e59467c4a2e43260129115bb2296909c05d16324d1e46caa2de671cf488265e8efa2f0cab6a183199490f27aec228b66ae0603b383828f7bb45458eb2632ccb018e37e00184630a1d1026a3584d197e7ff61558a35d1de157cd4af536bf104f51fb62a7b61f4524d44a202b4baf087d2f8278d4be6a4eb5672535deaedf5fb052dc43b8fec03d00b74a047edf2a2ba0acf00761882e098e16d558ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e5e10262566ea794a696222306a545a18f852a7163ccddc78fa4ab045d284e7638d5ccd00e93f487def22190efcb8872f6c01cd51d30ca160e95ceb289183f36faae02e26892c572843098734c461d675d9419b0999e6d97b8e3ac967e20c963ab0e64e559caba83211f8884bc10dc11b33fd53038b3421787dfb7826e30a7e3dfd17821d9df26e5f66cb3071c6b13f102a035521d989a76b396c7c6bd2b1756c0f9134178aeb9a3ebb0f857a84b24d7888d4b46f8674a812da946e68001baa6d1afcc161744dde6805547d28c7af7a3c8939c3426fa58106f0b3842954efe12a119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a161f2d562920394f5b3b7409ca85294c83559b0a08d94e2a1513dd27bf521a2b54d9985c0bb83026236cbe1e3e0cfa620bb16233f4d6f26833651f3dafa12c315445781384ef382665b1c032982dde07e54dd15f6a618f49be4fc37d28d69118cdbfd15bb5be012e93cdf04969bd3d0e21ee791282cd9d00fa77ef3faeb885495960c558ec2ab739835f8e076ce27a781e9b650fa4fed24c86fe373cc0e40f7bc4e4110a1c7ac33d7984b218b4c9bb7c91fa4c4740cdd900536f8072ac91027d8d20cf44896ef879cff74b5f96fbd74464e984795b7e120d1bf225061be9c010261f91322b0492425592045218ca963550d1e33d93398920f85e634592defb45f9e86f5d77ad3a740cacd53353a7f5507163756a11e3cc60b3ee88592e890f5b8e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e8dbc6f6bf42d8f15ca8eb874ad60e26b6934d23c9355f13d06926c18cc64d0379bb1ae1a9b93bd631fc79c3b5c1d4b235af15f633add00342eaac54c18c5b701a7473432d2322e433b25423ae283266497619703912c9548fbe06f68e9f7cb465b22195ab2e2406960c42948713e741190ed0a3d14582f1ce2148e5839364e4da33b414e1033b5260f0ed302fbadd337ccf56828469fcb5860ef132e13fc281b8dc2dc69f9beb43027421279eedf6158445ac81021fc1b0cdc6ceb456b18ab17492ab97b6fcfb81e6c88ea62a486c34897d604320e89ea2c35204e5858d587062235ad3e924d9a4b8733706a871a9b2cd4bf0831e3da073384741a14a0989316bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a257a8f469dc39a6c0d8201173a0d7f18f61e401ef3852712aac68309757af52582a9fd445133ce15b425a00fdcd3d721dc3f1f1e06bf29786d5da65ca698b07b1cb0e51ecea18879d02454382d65041f83faea2852882c4374fa085af9dff13c3e76377ab82853633d6cb4728e7cbb1387db9b67613ef26361fe4a1a5baab132d3380a112e6c4920050fea29d9820d1e7813fb4c6c144970097a9a6516059444661611459e9c9e73ffa97d33c3ed920807abf444b330424ec055d42e9818c86e23d8b84825961b027897c43ea2f394394a89043c2362c17e2b93ef18fb7c15618366b558fd3472509aec5771fae1b571b8e121280d5355090c85e11c894214668675ff1fedbf782641eae22da2f4eb630754fe0b5ad74c24f673452d2e2115777b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a2a8e3f34a6a2741c0c4c053f194edb6d6fcb854949b5766b8309490f51ab7a768c7585715a83f8654cb6a92a253a790de315730267ff8259224d431355f6332aa102ff5ea55e1d632e9d3d48f2599c4f36a51843773e0834755512077639ea2c3460676be50793586fba042f41cb030f4925d2630edc9557201f3b44bcca9d178d8fc3097a430472c905405c62bae1403d613a512bfa39413ab3d91f7f38a32d53e7b458e01a975187ce6e0bb1c47e6206c0ca5ec88503715d86a10592a0055141fa9d0675ac3f081c1cd00fc27f624daafd9064bfcedd0c4b20cf0b08d85c500a5efa6801f5163eab888b120114520dd147ed2cba630f2056c5995f1c1cfc54cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e350f2b29d90fec686f2bc56f9654d55ad4a8df01274b2e63a5bdc8644709960aaf595b513a634231ea74d441d72fd10f6f81e90761516e62f7c972128429a3022e60d16603fbae3750bcc26fa458170523007575787ac0666bb7c24434257f4339e22b294069374d3aef9852ddc07a68462e4c0f2a7e9c0187bf8a36664d40760609630ed3deb12e06c5b27c1bd3dd79d2cfd0563c6ca36970a7cd6ac7870548aa1c3f616f32fb082667872aaedcfa795842300583a0607904934e3cc86a8c211afcc161744dde6805547d28c7af7a3c8939c3426fa58106f0b3842954efe12a119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a199afc38830a9b1e06fe2364c3323125a2bf7c3a80d777061b87111d4624827ac1239d1755cf0d5e8f2d4a2060df180b38ba081f30dc284e29b2035788f6b752befceb3bd942a52ce7d5a3117f5a9f140aeb7f631efdbd210bb2263e8423076680497a5f52b8622b85a1da09dd824a7cdb60437c9572350cfa39ca0b3c27d55670fbba52f2bf4008b1235002b919863968a75b5a4e24461ca3e42134d9a8090d7ced2073dec21277a974021d4cd608020fbd5756543ca70fcd5b4c6b3c45b16181b9ca43397d8c1e5c4c1465cafa5b545a03445296934327ad87b85bd3477828119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21aabf8477c8f217024d3084741ff1ca224cab702339327c063ac9996586a71ee1d478f827af48ffa43b406d55ddd3d267a5256e60009e540508f42d40e8f7fa666579fe0090a58de5b4f90093d668f8a5b93673d4efd2dd03fb28eb812fefd2e571632b11aa5a01145d68f2c52ef364106325b4d5313a1e375897ccf2a9dc76e5d4fcf2c5e8b64e71ddfd12d7b7991351127945b04348c8f32cd933d73440e3f0153e7b458e01a975187ce6e0bb1c47e6206c0ca5ec88503715d86a10592a0055141fa9d0675ac3f081c1cd00fc27f624daafd9064bfcedd0c4b20cf0b08d85c500a5efa6801f5163eab888b120114520dd147ed2cba630f2056c5995f1c1cfc54cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e5588576061a86d65dc56ab2e21775d5f5c5d310053ebd21e6b65d527107e24558d5e3e2a44f8b82482583170b7b82866d552c221edd9771d3f6ea676cc354779ccbfa54c60c36613a3b4232e6754df757277062f9f24c36aa4b82f120901347ec8fb127e3ac43b532402154141760e52f9b2c6226a59161820a832758e54297ca64773390f010d0cf7d0126191ece242b239893af7468f679cec6e1c036603038c8b5312bbe48b79194a8d178cd62a6e4607c51f77e19b51efe92e7397a2df09ecbfb61868db5a682bb07564a4c988644d6f0110f530096c07758551311fb60ec3cb9b6cf8502c1f5f09701acb8729669abb09159dc8757504a71c6df7a1f61dc6de225be0251e349872372a71771c141028c61f2083546aeae9b453cb0d6a797b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a2b946f52edd7482402a7186aee598e0a5ef5a03115714679012d4f54ee5d6963830ddc22680703329777f14f26ca6a7cae5ebd43a885676f1ebae735f1de1f190777e33f6a6229712d486402e7b59b181f69970c7b9f4964540a274df4440c7555ea066804ac411b79a461345541fe2986512d63cf492f2dd7f03358be6f3123314c4758923d881257441d747bb4fc0a06b3982332578d5c5715bb73db713935edb98e2f4bd50e02910f68564e6bcb75c554d16db5f0053b9b88066f64bd9d4da158ff3f5c5f3353f03c4d71ce62e03baa94b97babc3313dcd0ba60c071137066d102c0d6720523a596f9e4e3bd72b335c1be1181d716825c21cac48e2abbd4aefa4dd115e6d5d36546d4671bd20df4d21e9f11d4505457afa210362cca7807419a3090bd394755658422f04002f000100000000005b7cfe5668d23301be40a376f066ba5895438370a7e5ce525cfe51712b8d251a38a18e5de26632512a11e21ff95ebf19cf9a50473327fc4b" } } ] @@ -131,98 +131,386 @@ "data": [ { "data": [ - 1124710164, - 363497165, - 1297563045, - 500796207, - 1985065864, - 1674334765, - 820663218, - 1236937246 + 1291233891, + 901611691, + 1515447763, + 518859210, + 691456689, + 1723616255, + 1740655893, + 1397350123 ] }, { "data": [ - 717506286, - 1222874154, - 247244907, - 1935061451, - 1353554446, - 1439044083, - 1124519971, - 559690834 + 1456518562, + 484811598, + 2010092021, + 282492124, + 1770180907, + 1769233778, + 494579282, + 1699827616 ] }, { "data": [ - 1358408385, - 1875364530, - 1799994181, - 1828281137, - 1706541869, - 1105434410, - 545311049, - 1911960662 + 704838371, + 2081776305, + 559183133, + 1733361779, + 263725425, + 344472513, + 2086779080, + 1240527530 ] }, { "data": [ - 950179274, - 1047614470, - 1624421018, - 1703569535, - 999754052, - 1679885826, - 1315761848, - 1898141320 + 1039415328, + 308461932, + 2043838459, + 611597666, + 1580071319, + 1516124162, + 698977291, + 1585930624 ] }, { "data": [ - 1740399675, - 1073633998, - 611042660, - 615002255, - 1235730188, - 87027280, - 86895955, - 887601845 + 250662127, + 1008190943, + 983708486, + 1247986374, + 1886580775, + 1647509743, + 1550488627, + 1260597451 ] }, { "data": [ - 136791005, - 222208790, - 211255601, - 658910733, - 1398974952, - 1962287981, - 1008967852, - 1459434081 + 416883050, + 188953242, + 1182024076, + 59202244, + 1978179518, + 1739410190, + 679526947, + 861617775 ] }, { "data": [ - 2058530742, - 1528131062, - 586506231, - 73007440, - 119264587, - 1734418534, - 763208946, - 11249856 + 1378484229, + 241452936, + 163273125, + 436861107, + 388667496, + 1797577532, + 443869040, + 906552846 ] }, { "data": [ - 1043531032, - 1880288654, - 2578972, - 266901381, - 656140631, - 1917379094, - 2056426869, - 705827472 + 799696786, + 1508418299, + 1856736097, + 1058842462, + 269359710, + 1099230447, + 749521578, + 520853695 + ] + }, + { + "data": [ + 439422050, + 1286915872, + 1358195170, + 840970194, + 1786302274, + 893515818, + 370457729, + 1427474800 + ] + }, + { + "data": [ + 2007504140, + 418866321, + 198684106, + 1996996870, + 1261455552, + 2118739919, + 56436890, + 1806594952 + ] + }, + { + "data": [ + 128324476, + 61519552, + 113352202, + 1839954511, + 2112962791, + 1831734346, + 1400107873, + 849776052 + ] + }, + { + "data": [ + 694706906, + 2057189854, + 1419202856, + 2020454400, + 1916412209, + 304600413, + 1119212112, + 988476852 + ] + }, + { + "data": [ + 1607491401, + 916185160, + 150839959, + 1915584536, + 1192630676, + 166752571, + 44618577, + 1961530862 + ] + }, + { + "data": [ + 1949287225, + 1766709295, + 928506169, + 833212136, + 35771750, + 71835570, + 1852857681, + 1205452729 + ] + }, + { + "data": [ + 1644443152, + 1520132256, + 1370044265, + 851862297, + 261020286, + 1001533477, + 571576626, + 907308311 + ] + }, + { + "data": [ + 1557846841, + 210200770, + 685212717, + 1586976910, + 463743886, + 395493034, + 1562290362, + 1016157604 + ] + }, + { + "data": [ + 208831676, + 1180089898, + 2064964824, + 1411007716, + 1673605982, + 1643551528, + 1539845891, + 704493341 + ] + }, + { + "data": [ + 895079925, + 877096130, + 2081347331, + 124656629, + 1179296144, + 1491205760, + 356412314, + 926452265 + ] + }, + { + "data": [ + 1422286144, + 984088526, + 1135304910, + 162305405, + 1064769342, + 1110991338, + 104215457, + 1422827345 + ] + }, + { + "data": [ + 912789203, + 2010420420, + 429286304, + 295855493, + 2084240709, + 1193367228, + 1021205972, + 560375846 + ] + }, + { + "data": [ + 489627247, + 396093595, + 475714912, + 573904495, + 382358549, + 668148792, + 1416579671, + 1444313453 + ] + }, + { + "data": [ + 1345967333, + 723445075, + 2048740831, + 153155071, + 10838758, + 1236457738, + 18985351, + 1138484833 + ] + }, + { + "data": [ + 993666071, + 473860152, + 482974488, + 1244638895, + 1287107597, + 1492708811, + 1976127099, + 653628523 + ] + }, + { + "data": [ + 791701066, + 885184677, + 106955182, + 1572481724, + 1456627534, + 1452427937, + 490533016, + 991293345 + ] + }, + { + "data": [ + 578639661, + 1631171923, + 268843612, + 1788996364, + 1746080381, + 1046103170, + 455298193, + 562115509 + ] + }, + { + "data": [ + 235948816, + 1018300141, + 1002498336, + 201831066, + 1124789148, + 1994905284, + 561014981, + 1257286951 + ] + }, + { + "data": [ + 1045385620, + 1058192257, + 938515492, + 573527403, + 989948080, + 1342850602, + 1832637791, + 358929324 + ] + }, + { + "data": [ + 1902152809, + 252599905, + 623219565, + 758434560, + 640896011, + 207032991, + 835792870, + 1665795896 + ] + }, + { + "data": [ + 723715685, + 587576367, + 853971724, + 1144944495, + 873175376, + 498689849, + 43297292, + 819091873 + ] + }, + { + "data": [ + 280016656, + 437742428, + 255947140, + 349343920, + 1615039346, + 1488983802, + 1389523623, + 1912297556 + ] + }, + { + "data": [ + 901881368, + 1526942608, + 852049680, + 731288118, + 661508501, + 2010590000, + 868332352, + 1726397935 + ] + }, + { + "data": [ + 1349685696, + 2130099561, + 1462674991, + 1479393751, + 1013840091, + 1746802826, + 422993280, + 544780634 ] } ] @@ -230,63 +518,783 @@ }, "rho": { "data": [ - 903205276, - 1258163451, - 499838896, - 838051028, - 1416916047, - 1156976355, - 1468982894 + 1708063334, + 500631902, + 912463888, + 1859022035, + 2093407176, + 589622141, + 421358791 ] }, "hashes": { "data": [ { "data": [ - 408448502, - 2097771261, - 1568864942, - 1711875409, - 932163598, - 1411104456, - 555220707, - 1889201513 + 1258012936, + 1759805387, + 1282523131, + 1087625667, + 2066885140, + 347413941, + 912179848, + 1182795675 + ] + }, + { + "data": [ + 2120621302, + 1146272237, + 220452956, + 1654122849, + 667076721, + 647293161, + 1473822684, + 610014272 + ] + }, + { + "data": [ + 588127966, + 1623774910, + 1981302286, + 1654815645, + 1739263748, + 476540846, + 1430988502, + 371554021 + ] + }, + { + "data": [ + 147575978, + 584959873, + 482088843, + 547413274, + 878230111, + 947438052, + 2065600800, + 1725116311 + ] + }, + { + "data": [ + 782401603, + 181111304, + 74795908, + 1495556562, + 2014424927, + 2103029287, + 626628827, + 915290649 + ] + }, + { + "data": [ + 1386657693, + 226764475, + 170886560, + 1391287227, + 241686273, + 1439085926, + 1299696477, + 224457038 + ] + }, + { + "data": [ + 156077062, + 984761927, + 636114116, + 2128285193, + 804007702, + 145764472, + 1829941080, + 897763155 + ] + }, + { + "data": [ + 832369921, + 238965180, + 160945039, + 1145687264, + 1389349131, + 1691977522, + 979195797, + 524772744 + ] + }, + { + "data": [ + 1305584230, + 1558936225, + 439307137, + 1771754638, + 875067293, + 1165888195, + 1802707435, + 1814799779 + ] + }, + { + "data": [ + 1584322090, + 1505637739, + 1431439512, + 2049043333, + 2031336532, + 1994220632, + 535798250, + 1003107923 + ] + }, + { + "data": [ + 536723909, + 1389453682, + 407278690, + 1778319839, + 1777727737, + 1948491210, + 1489215087, + 94425788 + ] + }, + { + "data": [ + 1939722636, + 1550150715, + 601277655, + 1185348003, + 205771634, + 1131394685, + 1434984545, + 971649311 + ] + }, + { + "data": [ + 1895618555, + 1937165542, + 1924755874, + 1626462217, + 1247425034, + 1370792545, + 10993310, + 259827571 + ] + }, + { + "data": [ + 960895278, + 1441935738, + 564122556, + 476582278, + 1152905344, + 187751495, + 1256558054, + 1184773204 + ] + }, + { + "data": [ + 1142655319, + 976140656, + 1227333160, + 2129498013, + 867861598, + 1790717609, + 866871241, + 1774362003 + ] + }, + { + "data": [ + 310323031, + 1437920355, + 878272104, + 722971220, + 1015159963, + 409582600, + 512066076, + 854680398 + ] + }, + { + "data": [ + 1078600753, + 1684508279, + 316224361, + 1222713314, + 336701486, + 1165314551, + 997088307, + 1438291675 + ] + }, + { + "data": [ + 1317260158, + 1861218639, + 1224575160, + 421550610, + 765270751, + 1827053147, + 289080869, + 538182924 + ] + }, + { + "data": [ + 191317502, + 911206352, + 127176973, + 1283200174, + 122086992, + 2069722074, + 1696651747, + 1805703619 + ] + }, + { + "data": [ + 1585522895, + 580813326, + 1019407832, + 475961126, + 2007366427, + 808496979, + 1181091986, + 697679912 + ] + }, + { + "data": [ + 1512587243, + 1963077188, + 1954992331, + 1545360150, + 1178760012, + 1515958126, + 705452917, + 2114456876 + ] + }, + { + "data": [ + 2049583212, + 1094272227, + 1039456282, + 787530139, + 1640279372, + 1330559514, + 240146549, + 1313599913 + ] + }, + { + "data": [ + 1716678544, + 878739389, + 47637648, + 1124863294, + 1855735812, + 648435874, + 1372962920, + 1357760622 + ] + }, + { + "data": [ + 1700444351, + 164566502, + 397969528, + 335079975, + 293991016, + 1078783808, + 326444266, + 1217021268 + ] + }, + { + "data": [ + 545406936, + 53426137, + 424114470, + 1111280438, + 618160273, + 1802384583, + 1667812411, + 783526014 + ] + }, + { + "data": [ + 305021847, + 844199180, + 1053002307, + 573437770, + 1003609966, + 752594751, + 1962990311, + 1014845114 + ] + }, + { + "data": [ + 1726913971, + 580369471, + 1458334500, + 153335379, + 781417921, + 1461588083, + 1878087297, + 1976620351 + ] + }, + { + "data": [ + 1342240957, + 1951347581, + 927989919, + 979795407, + 446565280, + 1833560107, + 1611077456, + 1708982869 + ] + }, + { + "data": [ + 1555733412, + 183459902, + 1845209457, + 294206894, + 487746799, + 1581300684, + 1098936144, + 1463828258 + ] + }, + { + "data": [ + 1627490533, + 1198683786, + 347829445, + 868233249, + 668381542, + 1667170279, + 1843656481, + 2118868916 + ] + }, + { + "data": [ + 25335971, + 1947476635, + 1202969731, + 1324303434, + 840681315, + 1530295647, + 73829885, + 2084868034 + ] + }, + { + "data": [ + 1384538139, + 1830543638, + 1993528212, + 829245670, + 987182524, + 1984193286, + 1630629317, + 671245330 + ] + }, + { + "data": [ + 1350732214, + 1458554923, + 1967947691, + 1326432866, + 2116862031, + 1830754813, + 1993865530, + 1629953044 + ] + }, + { + "data": [ + 1146542130, + 280817620, + 386152006, + 1428960819, + 1210084215, + 452674181, + 14651754, + 888508333 + ] + }, + { + "data": [ + 1560045092, + 1296963539, + 284985770, + 1434652130, + 229612754, + 1450040209, + 1958058095, + 1037043393 + ] + }, + { + "data": [ + 2020927885, + 18361940, + 653582762, + 1686120847, + 1597265575, + 28912714, + 443462147, + 870096418 + ] + }, + { + "data": [ + 1695586982, + 3373096, + 104141097, + 1042336897, + 994168241, + 1453130775, + 511038748, + 965536893 + ] + }, + { + "data": [ + 1605236949, + 127566776, + 21238712, + 434461941, + 1022175801, + 1240317127, + 1122138289, + 1008747176 + ] + }, + { + "data": [ + 2102377138, + 1530162129, + 909575023, + 1237305669, + 511960395, + 2038778105, + 287638646, + 545475552 + ] + }, + { + "data": [ + 123969828, + 595339923, + 285763600, + 1913417170, + 1555092419, + 2103200507, + 568212685, + 726567164 + ] + }, + { + "data": [ + 811207331, + 1566057452, + 346845733, + 1405783110, + 296074182, + 686180472, + 1562194090, + 1331754094 + ] + }, + { + "data": [ + 1195458345, + 1015303239, + 1769326913, + 1798476475, + 1959426322, + 263548056, + 1086173773, + 616986172 + ] + }, + { + "data": [ + 1679743849, + 267745726, + 813229082, + 1802821399, + 1106957379, + 681723311, + 38255328, + 119212296 + ] + }, + { + "data": [ + 538262759, + 561853307, + 1220138601, + 648920532, + 96368560, + 1848614699, + 564258293, + 877652518 + ] + }, + { + "data": [ + 236889586, + 1945633712, + 888366492, + 1363228903, + 1535081845, + 1843716973, + 870982648, + 2828223 + ] + }, + { + "data": [ + 1813717520, + 157322480, + 353732586, + 1058663683, + 767198951, + 185375665, + 1574055980, + 434808322 + ] + }, + { + "data": [ + 379825016, + 1815775610, + 718153065, + 878888419, + 2004655473, + 329280888, + 1716255418, + 2005381073 + ] + }, + { + "data": [ + 1590446408, + 1173249277, + 2092549673, + 208887188, + 912239485, + 796567703, + 274938304, + 390283874 + ] + }, + { + "data": [ + 779263010, + 747574741, + 1434583711, + 1620835829, + 1551673235, + 1284998639, + 679093843, + 1406669023 + ] + }, + { + "data": [ + 1291148586, + 1081265798, + 1526996412, + 391781492, + 1711281276, + 1313014433, + 1384242624, + 623027609 + ] + }, + { + "data": [ + 953015683, + 934645423, + 1771313714, + 470438654, + 1645632988, + 1239732071, + 688694286, + 1693593789 + ] + }, + { + "data": [ + 1677902343, + 45276613, + 2103891579, + 1112027086, + 161866262, + 1434591076, + 1951598120, + 772846762 + ] + }, + { + "data": [ + 551705762, + 1871931766, + 1065697665, + 283086151, + 2053411512, + 1094840383, + 1766312832, + 256750162 + ] + }, + { + "data": [ + 429680689, + 125824827, + 1965715718, + 2057352154, + 1776082615, + 2118510694, + 176499827, + 1212838505 + ] + }, + { + "data": [ + 1556722792, + 1298468122, + 657266497, + 1348176792, + 97032780, + 432903656, + 1713460397, + 236087742 + ] + }, + { + "data": [ + 268618238, + 781464872, + 1629401850, + 2084984500, + 1651362468, + 871068115, + 1722302961, + 712459750 + ] + }, + { + "data": [ + 1361188658, + 539241318, + 1966654298, + 1775313608, + 143728868, + 1546419295, + 665328088, + 2041591993 + ] + }, + { + "data": [ + 1660687505, + 535693535, + 1188238224, + 1910372027, + 441895189, + 208597526, + 1637375248, + 1439508567 + ] + }, + { + "data": [ + 1139697001, + 555025515, + 1728548969, + 1245647761, + 1604041527, + 1752808772, + 1419902779, + 1640507729 + ] + }, + { + "data": [ + 1960240298, + 666218643, + 1280441627, + 1940051826, + 1775703419, + 598652016, + 140095253, + 829013884 + ] + }, + { + "data": [ + 303840967, + 363183783, + 2079196084, + 1077588941, + 1843884294, + 229585661, + 84469314, + 1923645935 ] }, { "data": [ - 1844609483, - 897825857, - 972574515, - 1126889766, - 1803958599, - 1181767797, - 685933363, - 1742010269 + 1487940169, + 725658218, + 1422188831, + 2055497525, + 1396855667, + 456348791, + 1027525060, + 1026406513 ] }, { "data": [ - 1849815147, - 68889604, - 429648865, - 451003460, - 779010014, - 1971790217, - 653377049, - 1596905928 + 1435497940, + 276128380, + 1036933776, + 678450869, + 1197285788, + 816650348, + 240096989, + 898816825 ] }, { "data": [ - 2094660960, - 751870991, - 1524987780, - 436778235, - 1066607152, - 2047885417, - 142725384, - 2117521627 + 248028907, + 940423932, + 2017860464, + 1112538086, + 866251675, + 135676603, + 1729849157, + 73108520 ] } ] @@ -295,10 +1303,10 @@ } }, "_info": { - "hash": "0x2c37ce0a085724b8413d48661004f9e8deae40f786274ff59c6b05fc80a1d61d", + "hash": "0xea422f16f99bc1b58fe5c1d13bc2f75c9b3b2719cdc3c40c495da9f131f2fe85", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet]", - "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - 3 validators in the genesis state\n - 2 additional attestations from validators 0 and 2 (in addition to proposer)\n - Verifies that all signatures are generated correctly\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n 2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This test verifies multi-validator signature scenarios:\n - Multiple XMSS keys are generated for different validators\n - Attestations from non-proposer validators are correctly verified\n - Signature aggregation works with multiple attestations (signature positions are correct)", + "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- 3 validators in the genesis state\n- 2 additional attestations from validators 0 and 2 (in addition to proposer)\n- Verifies that all signatures are generated correctly\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis test verifies multi-validator signature scenarios:\n- Multiple XMSS keys are generated for different validators\n- Attestations from non-proposer validators are correctly verified\n- Signature aggregation works with multiple attestations (signature positions are correct)", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json index f86d8a7..4967d3a 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet][fork_devnet-verify_signatures_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,11 +31,11 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 } ] @@ -52,8 +52,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0x67c920f466c1d4d6f9674f33d7c937bef725acdb07ff9024de863b2f32bddc68", - "stateRoot": "0x767763ba4284e0fb9379e182c90d1687787e83b2f4e0c62407249ea3a104b27a", + "parentRoot": "0x25f6beaf778b5d1cad9d33b9bc39f7d2ed521cb3f03d8b086b6af8408617635a", + "stateRoot": "0xf8947796ac01c5ab946e51321a263b897421f03497964007001ca86e15ee4c8d", "body": { "attestations": { "data": [] @@ -65,15 +65,15 @@ "data": { "slot": 1, "head": { - "root": "0x012023705f5384f54c111656e1d08bab57f61ff3814c83661227c4886fdbc7a1", + "root": "0x16e2e465a92b9d884438754ce66e3b04e25bce25bd76ab182f8dba7502a4aca6", "slot": 1 }, "target": { - "root": "0x012023705f5384f54c111656e1d08bab57f61ff3814c83661227c4886fdbc7a1", + "root": "0x16e2e465a92b9d884438754ce66e3b04e25bce25bd76ab182f8dba7502a4aca6", "slot": 1 }, "source": { - "root": "0x67c920f466c1d4d6f9674f33d7c937bef725acdb07ff9024de863b2f32bddc68", + "root": "0x25f6beaf778b5d1cad9d33b9bc39f7d2ed521cb3f03d8b086b6af8408617635a", "slot": 0 } } @@ -89,98 +89,386 @@ "data": [ { "data": [ - 1124710164, - 363497165, - 1297563045, - 500796207, - 1985065864, - 1674334765, - 820663218, - 1236937246 + 1291233891, + 901611691, + 1515447763, + 518859210, + 691456689, + 1723616255, + 1740655893, + 1397350123 ] }, { "data": [ - 717506286, - 1222874154, - 247244907, - 1935061451, - 1353554446, - 1439044083, - 1124519971, - 559690834 + 1456518562, + 484811598, + 2010092021, + 282492124, + 1770180907, + 1769233778, + 494579282, + 1699827616 ] }, { "data": [ - 1358408385, - 1875364530, - 1799994181, - 1828281137, - 1706541869, - 1105434410, - 545311049, - 1911960662 + 704838371, + 2081776305, + 559183133, + 1733361779, + 263725425, + 344472513, + 2086779080, + 1240527530 ] }, { "data": [ - 950179274, - 1047614470, - 1624421018, - 1703569535, - 999754052, - 1679885826, - 1315761848, - 1898141320 + 1039415328, + 308461932, + 2043838459, + 611597666, + 1580071319, + 1516124162, + 698977291, + 1585930624 ] }, { "data": [ - 1740399675, - 1073633998, - 611042660, - 615002255, - 1235730188, - 87027280, - 86895955, - 887601845 + 250662127, + 1008190943, + 983708486, + 1247986374, + 1886580775, + 1647509743, + 1550488627, + 1260597451 ] }, { "data": [ - 136791005, - 222208790, - 211255601, - 658910733, - 1398974952, - 1962287981, - 1008967852, - 1459434081 + 416883050, + 188953242, + 1182024076, + 59202244, + 1978179518, + 1739410190, + 679526947, + 861617775 ] }, { "data": [ - 2058530742, - 1528131062, - 586506231, - 73007440, - 119264587, - 1734418534, - 763208946, - 11249856 + 1378484229, + 241452936, + 163273125, + 436861107, + 388667496, + 1797577532, + 443869040, + 906552846 ] }, { "data": [ - 1043531032, - 1880288654, - 2578972, - 266901381, - 656140631, - 1917379094, - 2056426869, - 705827472 + 799696786, + 1508418299, + 1856736097, + 1058842462, + 269359710, + 1099230447, + 749521578, + 520853695 + ] + }, + { + "data": [ + 439422050, + 1286915872, + 1358195170, + 840970194, + 1786302274, + 893515818, + 370457729, + 1427474800 + ] + }, + { + "data": [ + 2007504140, + 418866321, + 198684106, + 1996996870, + 1261455552, + 2118739919, + 56436890, + 1806594952 + ] + }, + { + "data": [ + 128324476, + 61519552, + 113352202, + 1839954511, + 2112962791, + 1831734346, + 1400107873, + 849776052 + ] + }, + { + "data": [ + 694706906, + 2057189854, + 1419202856, + 2020454400, + 1916412209, + 304600413, + 1119212112, + 988476852 + ] + }, + { + "data": [ + 1607491401, + 916185160, + 150839959, + 1915584536, + 1192630676, + 166752571, + 44618577, + 1961530862 + ] + }, + { + "data": [ + 1949287225, + 1766709295, + 928506169, + 833212136, + 35771750, + 71835570, + 1852857681, + 1205452729 + ] + }, + { + "data": [ + 1644443152, + 1520132256, + 1370044265, + 851862297, + 261020286, + 1001533477, + 571576626, + 907308311 + ] + }, + { + "data": [ + 1557846841, + 210200770, + 685212717, + 1586976910, + 463743886, + 395493034, + 1562290362, + 1016157604 + ] + }, + { + "data": [ + 208831676, + 1180089898, + 2064964824, + 1411007716, + 1673605982, + 1643551528, + 1539845891, + 704493341 + ] + }, + { + "data": [ + 895079925, + 877096130, + 2081347331, + 124656629, + 1179296144, + 1491205760, + 356412314, + 926452265 + ] + }, + { + "data": [ + 1422286144, + 984088526, + 1135304910, + 162305405, + 1064769342, + 1110991338, + 104215457, + 1422827345 + ] + }, + { + "data": [ + 912789203, + 2010420420, + 429286304, + 295855493, + 2084240709, + 1193367228, + 1021205972, + 560375846 + ] + }, + { + "data": [ + 489627247, + 396093595, + 475714912, + 573904495, + 382358549, + 668148792, + 1416579671, + 1444313453 + ] + }, + { + "data": [ + 1345967333, + 723445075, + 2048740831, + 153155071, + 10838758, + 1236457738, + 18985351, + 1138484833 + ] + }, + { + "data": [ + 993666071, + 473860152, + 482974488, + 1244638895, + 1287107597, + 1492708811, + 1976127099, + 653628523 + ] + }, + { + "data": [ + 791701066, + 885184677, + 106955182, + 1572481724, + 1456627534, + 1452427937, + 490533016, + 991293345 + ] + }, + { + "data": [ + 578639661, + 1631171923, + 268843612, + 1788996364, + 1746080381, + 1046103170, + 455298193, + 562115509 + ] + }, + { + "data": [ + 235948816, + 1018300141, + 1002498336, + 201831066, + 1124789148, + 1994905284, + 561014981, + 1257286951 + ] + }, + { + "data": [ + 1045385620, + 1058192257, + 938515492, + 573527403, + 989948080, + 1342850602, + 1832637791, + 358929324 + ] + }, + { + "data": [ + 1902152809, + 252599905, + 623219565, + 758434560, + 640896011, + 207032991, + 835792870, + 1665795896 + ] + }, + { + "data": [ + 723715685, + 587576367, + 853971724, + 1144944495, + 873175376, + 498689849, + 43297292, + 819091873 + ] + }, + { + "data": [ + 280016656, + 437742428, + 255947140, + 349343920, + 1615039346, + 1488983802, + 1389523623, + 1912297556 + ] + }, + { + "data": [ + 901881368, + 1526942608, + 852049680, + 731288118, + 661508501, + 2010590000, + 868332352, + 1726397935 + ] + }, + { + "data": [ + 1349685696, + 2130099561, + 1462674991, + 1479393751, + 1013840091, + 1746802826, + 422993280, + 544780634 ] } ] @@ -188,63 +476,783 @@ }, "rho": { "data": [ - 644208543, - 218416544, - 529253169, - 1007502172, - 403215989, - 26113621, - 822584767 + 716203521, + 581420617, + 1286685526, + 1194695558, + 1641401137, + 1997614743, + 496514183 ] }, "hashes": { "data": [ { "data": [ - 373907450, - 1905897425, - 398616161, - 118479090, - 628416889, - 558057520, - 531733487, - 764291998 + 1007398320, + 1251646982, + 1612967266, + 537381478, + 93386731, + 1083280595, + 1721356609, + 1286371566 + ] + }, + { + "data": [ + 170535041, + 1162715015, + 694935546, + 306154186, + 1318628375, + 1972574425, + 1164163541, + 93198021 + ] + }, + { + "data": [ + 975961833, + 531824562, + 2035811416, + 1364843342, + 322668550, + 458809832, + 349604618, + 2092803528 + ] + }, + { + "data": [ + 654742765, + 1159278164, + 1972996313, + 523872121, + 1805821337, + 124485604, + 1193056330, + 807117735 + ] + }, + { + "data": [ + 919568569, + 2007737629, + 1957321079, + 1976407600, + 533410046, + 2111488436, + 1620339375, + 801239907 + ] + }, + { + "data": [ + 1248809976, + 857619471, + 1558705607, + 1000070635, + 536413566, + 1646494315, + 1742462035, + 361896064 + ] + }, + { + "data": [ + 1984415707, + 2120523866, + 460541868, + 1401766703, + 191855557, + 62277793, + 839423381, + 1933336793 + ] + }, + { + "data": [ + 720993176, + 565557239, + 144294658, + 1965029513, + 1320601105, + 1033475156, + 924580775, + 1891983182 + ] + }, + { + "data": [ + 2107882818, + 42805544, + 1038376944, + 1485088008, + 413502243, + 595063755, + 208296301, + 137522358 + ] + }, + { + "data": [ + 1584322090, + 1505637739, + 1431439512, + 2049043333, + 2031336532, + 1994220632, + 535798250, + 1003107923 + ] + }, + { + "data": [ + 536723909, + 1389453682, + 407278690, + 1778319839, + 1777727737, + 1948491210, + 1489215087, + 94425788 + ] + }, + { + "data": [ + 1472227604, + 777167222, + 1014016292, + 2108428626, + 1572854930, + 936945519, + 1087148360, + 239564935 + ] + }, + { + "data": [ + 1895618555, + 1937165542, + 1924755874, + 1626462217, + 1247425034, + 1370792545, + 10993310, + 259827571 + ] + }, + { + "data": [ + 1633133337, + 1030126208, + 1634656506, + 1714432182, + 923722773, + 1592896262, + 1045605719, + 1004996167 + ] + }, + { + "data": [ + 1142655319, + 976140656, + 1227333160, + 2129498013, + 867861598, + 1790717609, + 866871241, + 1774362003 + ] + }, + { + "data": [ + 310323031, + 1437920355, + 878272104, + 722971220, + 1015159963, + 409582600, + 512066076, + 854680398 + ] + }, + { + "data": [ + 562156248, + 407660686, + 123830526, + 1475625115, + 2009710949, + 611229674, + 227976023, + 1979398321 + ] + }, + { + "data": [ + 1317260158, + 1861218639, + 1224575160, + 421550610, + 765270751, + 1827053147, + 289080869, + 538182924 + ] + }, + { + "data": [ + 1373100955, + 1325801240, + 242486397, + 1594220983, + 1224055938, + 1039685355, + 1369882156, + 2105201870 + ] + }, + { + "data": [ + 882140143, + 972100438, + 1911957230, + 1132707040, + 1927731179, + 1221461913, + 7060907, + 1965761976 + ] + }, + { + "data": [ + 1512587243, + 1963077188, + 1954992331, + 1545360150, + 1178760012, + 1515958126, + 705452917, + 2114456876 + ] + }, + { + "data": [ + 2049583212, + 1094272227, + 1039456282, + 787530139, + 1640279372, + 1330559514, + 240146549, + 1313599913 + ] + }, + { + "data": [ + 1405173867, + 1816274123, + 914189354, + 1390194868, + 1875873291, + 729884524, + 1391291848, + 712390226 + ] + }, + { + "data": [ + 598163318, + 669166894, + 2095183582, + 2020485494, + 1353088122, + 288048132, + 219781410, + 2002759719 + ] + }, + { + "data": [ + 545406936, + 53426137, + 424114470, + 1111280438, + 618160273, + 1802384583, + 1667812411, + 783526014 + ] + }, + { + "data": [ + 880153551, + 1844784642, + 958326447, + 604503257, + 1004388263, + 410341879, + 1891463524, + 1293918805 + ] + }, + { + "data": [ + 501558010, + 740311244, + 1050231400, + 540493697, + 1939025889, + 865101441, + 450874438, + 556623367 + ] + }, + { + "data": [ + 14617393, + 1741137626, + 1232402672, + 1297835855, + 180473396, + 296331666, + 678243236, + 1349472775 + ] + }, + { + "data": [ + 1555733412, + 183459902, + 1845209457, + 294206894, + 487746799, + 1581300684, + 1098936144, + 1463828258 + ] + }, + { + "data": [ + 359145510, + 91313123, + 46174230, + 237526521, + 2072622391, + 1873031895, + 797080303, + 1306795272 + ] + }, + { + "data": [ + 150143807, + 1240854373, + 2107166892, + 1239041875, + 199965884, + 143519120, + 877927432, + 463537849 + ] + }, + { + "data": [ + 1279960983, + 1843209453, + 1821049971, + 1629719253, + 1678547126, + 1467417183, + 2126155977, + 29064399 + ] + }, + { + "data": [ + 2087574544, + 1055748285, + 1878614231, + 639879396, + 1324171225, + 673620585, + 1341934876, + 558856819 + ] + }, + { + "data": [ + 1584783469, + 1012855306, + 401348507, + 2091865749, + 1821226690, + 741147898, + 257249483, + 1263361762 + ] + }, + { + "data": [ + 1067822535, + 1891280943, + 813430117, + 1276106738, + 1193107083, + 1477156918, + 1539220620, + 96504038 + ] + }, + { + "data": [ + 2020927885, + 18361940, + 653582762, + 1686120847, + 1597265575, + 28912714, + 443462147, + 870096418 + ] + }, + { + "data": [ + 1393564690, + 2099610787, + 1433569094, + 1415341075, + 667347082, + 534542891, + 2086215618, + 311924734 + ] + }, + { + "data": [ + 1605236949, + 127566776, + 21238712, + 434461941, + 1022175801, + 1240317127, + 1122138289, + 1008747176 + ] + }, + { + "data": [ + 855427493, + 1207280363, + 122948393, + 1858476858, + 717680189, + 297650565, + 1852129145, + 498572861 + ] + }, + { + "data": [ + 123969828, + 595339923, + 285763600, + 1913417170, + 1555092419, + 2103200507, + 568212685, + 726567164 + ] + }, + { + "data": [ + 479007558, + 124272114, + 1475575201, + 470022555, + 470436106, + 1311385231, + 790477535, + 123444182 + ] + }, + { + "data": [ + 1851254867, + 1287818641, + 1976227070, + 2000161771, + 366470125, + 1781542280, + 130581790, + 1069901828 + ] + }, + { + "data": [ + 1246563804, + 1413964901, + 928709195, + 135099607, + 1933313698, + 942190559, + 1583299962, + 405273537 + ] + }, + { + "data": [ + 15025568, + 798031475, + 1319692199, + 626923173, + 639980038, + 1042881228, + 1087868684, + 1534522887 + ] + }, + { + "data": [ + 236889586, + 1945633712, + 888366492, + 1363228903, + 1535081845, + 1843716973, + 870982648, + 2828223 + ] + }, + { + "data": [ + 668744770, + 1762680521, + 777619164, + 842438923, + 2088233233, + 1413163967, + 2016710389, + 1505732360 + ] + }, + { + "data": [ + 428137177, + 180423701, + 422464102, + 2076710433, + 1729838960, + 535452591, + 908577683, + 35856264 + ] + }, + { + "data": [ + 1967540513, + 1519776050, + 2036007845, + 893366942, + 2089822704, + 856708567, + 52673874, + 1680933072 + ] + }, + { + "data": [ + 981208312, + 1084818190, + 677102979, + 2063847281, + 1364366814, + 1457677810, + 1899058168, + 1563619590 + ] + }, + { + "data": [ + 1291148586, + 1081265798, + 1526996412, + 391781492, + 1711281276, + 1313014433, + 1384242624, + 623027609 + ] + }, + { + "data": [ + 200567419, + 388962948, + 476521573, + 687024916, + 1833415520, + 1730904880, + 443398259, + 436157135 + ] + }, + { + "data": [ + 707230432, + 1154831848, + 281037294, + 1768624406, + 430016495, + 1598391594, + 533135163, + 1005066409 + ] + }, + { + "data": [ + 1575751610, + 754993504, + 976076502, + 28864881, + 203441435, + 1815755927, + 2032423475, + 1425030338 + ] + }, + { + "data": [ + 1562339170, + 1308262206, + 27630180, + 395740765, + 2013010851, + 1820364392, + 1927685629, + 104952625 + ] + }, + { + "data": [ + 1337781915, + 946317826, + 736367261, + 1158536580, + 1619326108, + 291263633, + 543599065, + 2111323116 + ] + }, + { + "data": [ + 252041760, + 1817414500, + 1222652907, + 729393086, + 1201147464, + 204350039, + 1423174574, + 766473914 + ] + }, + { + "data": [ + 35290624, + 543962937, + 163333824, + 329618781, + 896461446, + 346705117, + 690957194, + 1923687483 + ] + }, + { + "data": [ + 2113056063, + 1632595436, + 1471738161, + 732872543, + 1177191142, + 1142007075, + 1194993142, + 1559467243 + ] + }, + { + "data": [ + 1982272307, + 992599898, + 912060509, + 756026196, + 1317365254, + 900172012, + 1887616520, + 86671560 + ] + }, + { + "data": [ + 2046630080, + 888883591, + 84025767, + 77874323, + 1407868461, + 443546501, + 203936347, + 412038982 + ] + }, + { + "data": [ + 1135271242, + 490412616, + 1384616381, + 596851392, + 371643044, + 98437837, + 390163320, + 374739258 ] }, { "data": [ - 1055073502, - 938359962, - 1750550172, - 392088656, - 1252599961, - 1636709997, - 568483147, - 1846461560 + 1465916835, + 1762678201, + 1111080241, + 460605314, + 1685336972, + 120785414, + 1657833535, + 1990363046 ] }, { "data": [ - 654587533, - 2002784255, - 1460976588, - 1115305527, - 961242847, - 746080331, - 1518778449, - 931391832 + 1622723517, + 1868134833, + 1357640922, + 2093289686, + 1317449817, + 1456398689, + 865029087, + 990587183 ] }, { "data": [ - 1409671451, - 1790541359, - 440247838, - 1460276156, - 1516748941, - 1343472744, - 331072020, - 682552546 + 989042468, + 1523246160, + 349574826, + 1340646482, + 1579176982, + 725957665, + 514279194, + 572149168 ] } ] @@ -253,10 +1261,10 @@ } }, "_info": { - "hash": "0x94f9c136e620fd57a2be24e462bbb6d02ec31d8ea3b1d8141b09fd500c881b01", + "hash": "0xfd453261ae39cec510a775702a42457e8aefbf018d4e32cad082b20bede7a8ae", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet]", - "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This is the most basic signature generation test. It verifies:\n - XMSS key generation works\n - Signature aggregation includes proposer signature", + "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis is the most basic signature generation test. It verifies:\n- XMSS key generation works\n- Signature aggregation includes proposer signature", "fixtureFormat": "verify_signatures_test" } } From 8f810df573473aa60fc4b4b75fcd0867e3063a3f Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Sat, 24 Jan 2026 10:32:43 +0200 Subject: [PATCH 14/48] updated test vector deserialization to correctly handle exceptions --- .../containers/tests/test_vectors/runner.rs | 53 +++++++++++++------ .../tests/test_vectors/verify_signatures.rs | 5 +- .../test_invalid_signature.json | 18 +++---- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index 7459538..bcc91f4 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -626,8 +626,32 @@ impl TestRunner { ) -> Result<(), Box> { let json_content = fs::read_to_string(path.as_ref())?; - // Parse using the VerifySignaturesTestVectorFile structure - let test_file: VerifySignaturesTestVectorFile = serde_json::from_str(&json_content)?; + // Phase 1: parse minimally to detect `expectException` even if typed parsing fails. + let raw: serde_json::Value = serde_json::from_str(&json_content)?; + + let expect_exception = raw + .get("tests") + .and_then(|t| t.as_object()) + .and_then(|obj| obj.values().next()) + .and_then(|tc| tc.get("expectException")) + .and_then(|v| v.as_str()) + .map(|s| s.to_owned()); + + // Phase 2: parse into the typed structure. + let test_file: VerifySignaturesTestVectorFile = match serde_json::from_str(&json_content) { + Ok(v) => v, + Err(e) => { + if let Some(ref ex) = expect_exception { + println!( + "\nExpected exception: {} (typed JSON parse failed: {})", + ex, e + ); + println!("\n\x1b[32m✓ PASS\x1b[0m\n"); + return Ok(()); + } + return Err(Box::new(e)); + } + }; // Get the first (and only) test case from the file let (test_name, test_case) = test_file @@ -665,23 +689,22 @@ impl TestRunner { if result { println!(" \x1b[31m✗ FAIL: Signatures verified successfully but should have failed!\x1b[0m\n"); return Err("Expected signature verification to fail, but it succeeded".into()); - } else { - println!(" ✓ Correctly rejected: Invalid signatures detected"); - println!("\n\x1b[32m✓ PASS\x1b[0m\n"); } - } else { - // Valid test case - signatures should verify successfully - let result = signed_block.verify_signatures(anchor_state); - if result { - println!(" ✓ All signatures verified successfully"); - println!("\n\x1b[32m✓ PASS\x1b[0m\n"); - } else { - println!(" \x1b[31m✗ FAIL: Signature verification failed\x1b[0m\n"); - return Err("Signature verification failed".into()); - } + println!(" ✓ Correctly rejected: Invalid signatures detected"); + println!("\n\x1b[32m✓ PASS\x1b[0m\n"); + return Ok(()); } + let result = signed_block.verify_signatures(anchor_state); + if !result { + println!(" \x1b[31m✗ FAIL: Signature verification failed\x1b[0m\n"); + return Err("Signature verification failed".into()); + } + + println!(" ✓ All signatures verified successfully"); + println!("\n\x1b[32m✓ PASS\x1b[0m\n"); Ok(()) } + } diff --git a/lean_client/containers/tests/test_vectors/verify_signatures.rs b/lean_client/containers/tests/test_vectors/verify_signatures.rs index 7e52cff..624f7d0 100644 --- a/lean_client/containers/tests/test_vectors/verify_signatures.rs +++ b/lean_client/containers/tests/test_vectors/verify_signatures.rs @@ -12,10 +12,11 @@ use test_generator::test_resources; use super::runner::TestRunner; #[test_resources("test_vectors/verify_signatures/*/verify_signatures/*/*.json")] -fn verify_signatures(spec_file: &str) { +fn verify_signatures(spec_file: &str) -> Result<(), Box> { let test_path = Path::new(env!("CARGO_MANIFEST_DIR")) .join("..") .join(spec_file); - TestRunner::run_verify_signatures_test(test_path).unwrap(); + TestRunner::run_verify_signatures_test(test_path)?; + Ok(()) } diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json index 773a283..cc95a6e 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet][fork_devnet-verify_signatures_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,7 +31,7 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 } ] @@ -48,8 +48,8 @@ "block": { "slot": 1, "proposerIndex": 0, - "parentRoot": "0xb6f6683bf01027dd60f095f477b8ca38fbe23c18eb02f0aed0b2f34b7a4584f0", - "stateRoot": "0x6c53c1d71203251ceb0abcb1d14a34b53c0760abc1b1ac51d509cbe16d8ceb16", + "parentRoot": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", + "stateRoot": "0x38d7edaf9c08ffb285eb3e1e64456fbe430d4c4a4bab9b0bf29565b74da5c620", "body": { "attestations": { "data": [] @@ -61,15 +61,15 @@ "data": { "slot": 1, "head": { - "root": "0xcdc120c88d251c898fc9b9a1f091b0cb108d9ac82d2784029917c2ac3cee82ee", + "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", "slot": 1 }, "target": { - "root": "0xcdc120c88d251c898fc9b9a1f091b0cb108d9ac82d2784029917c2ac3cee82ee", + "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", "slot": 1 }, "source": { - "root": "0xb6f6683bf01027dd60f095f477b8ca38fbe23c18eb02f0aed0b2f34b7a4584f0", + "root": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", "slot": 0 } } @@ -104,10 +104,10 @@ }, "expectException": "AssertionError", "_info": { - "hash": "0x0d7dbcbd6fd7a106e16128e65e3650693380131c8864455f3a9e2c1003f710ba", + "hash": "0x8d97e6b6a601e10856ae70720ffad8cd392dab8169fe158054edac0bc0f0e49a", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet]", - "description": "Test that invalid signatures are properly rejected during verification.\n\n Scenario\n --------\n - Single block at slot 1\n - Proposer attestation has an invalid signature\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation is rejected\n\n Why This Matters\n ----------------\n This test verifies the negative case:\n - Signature verification actually validates cryptographic correctness\n not just structural correctness.\n - Invalid signatures are caught, not silently accepted", + "description": "Test that invalid signatures are properly rejected during verification.\n\nScenario\n--------\n- Single block at slot 1\n- Proposer attestation has an invalid signature\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation is rejected\n\nWhy This Matters\n----------------\nThis test verifies the negative case:\n- Signature verification actually validates cryptographic correctness\n not just structural correctness.\n- Invalid signatures are caught, not silently accepted", "fixtureFormat": "verify_signatures_test" } } From 985579c433ead61a3f94e2541c1a832844d548f7 Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Sat, 24 Jan 2026 14:29:04 +0200 Subject: [PATCH 15/48] fix formatting --- lean_client/containers/tests/test_vectors/runner.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index bcc91f4..6192dad 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -706,5 +706,4 @@ impl TestRunner { println!("\n\x1b[32m✓ PASS\x1b[0m\n"); Ok(()) } - } From 0f6543841e16301df7a9557962047c4c204b45e9 Mon Sep 17 00:00:00 2001 From: Nojus Sandovas Date: Fri, 23 Jan 2026 22:12:13 +0200 Subject: [PATCH 16/48] Removed dead code with devnet-2 feature flags, removed unused imports in validator/lib.rs and moved the unit_tests to separate folder and file --- .../tests/unit_tests/state_transition.rs | 26 --------- lean_client/validator/src/lib.rs | 58 +------------------ lean_client/validator/tests/unit_tests.rs | 38 ++++++++++++ 3 files changed, 39 insertions(+), 83 deletions(-) create mode 100644 lean_client/validator/tests/unit_tests.rs diff --git a/lean_client/containers/tests/unit_tests/state_transition.rs b/lean_client/containers/tests/unit_tests/state_transition.rs index 4b763c6..bcf0ea1 100644 --- a/lean_client/containers/tests/unit_tests/state_transition.rs +++ b/lean_client/containers/tests/unit_tests/state_transition.rs @@ -40,19 +40,6 @@ fn test_state_transition_full() { let expected_state = state_after_header.process_attestations(&block.body.attestations); - #[cfg(feature = "devnet2")] - let expected_state = { - let mut unaggregated_attestations = Attestations::default(); - for aggregated_attestation in &block.body.attestations { - let plain_attestations = aggregated_attestation.to_plain(); - // For each attestatio in the vector, push to the list - for attestation in plain_attestations { - unaggregated_attestations.push(attestation); - } - } - state_after_header.process_attestations(&unaggregated_attestations) - }; - let block_with_correct_root = Block { state_root: hash_tree_root(&expected_state), ..block @@ -87,19 +74,6 @@ fn test_state_transition_invalid_signatures() { let expected_state = state_after_header.process_attestations(&block.body.attestations); - #[cfg(feature = "devnet2")] - let expected_state = { - let mut list = Attestations::default(); - for aggregated_attestation in &block.body.attestations { - let plain_attestations = aggregated_attestation.to_plain(); - // For each attestatio in the vector, push to the list - for attestation in plain_attestations { - list.push(attestation); - } - } - list - }; - let block_with_correct_root = Block { state_root: hash_tree_root(&expected_state), ..block diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index 14ab340..cde7b17 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -9,7 +9,7 @@ use containers::{ block::{hash_tree_root, BlockWithAttestation, SignedBlockWithAttestation}, checkpoint::Checkpoint, types::{Uint64, ValidatorIndex}, - AggregatedAttestation, Slot, + Slot, }; use fork_choice::store::{get_proposal_head, get_vote_target, Store}; use tracing::{info, warn}; @@ -191,12 +191,6 @@ impl ValidatorService { .map(|att| att.message.clone()) .collect(); - #[cfg(feature = "devnet2")] - let valid_attestations: Vec = valid_signed_attestations - .iter() - .map(|att| att.message.clone()) - .collect(); - info!( slot = slot.0, valid_attestations = valid_attestations.len(), @@ -325,14 +319,6 @@ impl ValidatorService { source: store.latest_justified.clone(), }; - #[cfg(feature = "devnet2")] - let attestation = AttestationData { - slot, - head: head_checkpoint.clone(), - target: vote_target.clone(), - source: store.latest_justified.clone(), - }; - let signature = if let Some(ref key_manager) = self.key_manager { // Sign with XMSS let message = hash_tree_root(&attestation); @@ -379,45 +365,3 @@ impl ValidatorService { .collect() } } - -// DI GENERUOTI TESTAI -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_proposer_selection() { - let config = ValidatorConfig { - node_id: "test_0".to_string(), - validator_indices: vec![2], - }; - let service = ValidatorService::new(config, 4); - - // Validator 2 should propose at slots 2, 6, 10, ... - assert!(service.get_proposer_for_slot(Slot(2)).is_some()); - assert!(service.get_proposer_for_slot(Slot(6)).is_some()); - assert!(service.get_proposer_for_slot(Slot(10)).is_some()); - - // Validator 2 should NOT propose at slots 0, 1, 3, 4, 5, ... - assert!(service.get_proposer_for_slot(Slot(0)).is_none()); - assert!(service.get_proposer_for_slot(Slot(1)).is_none()); - assert!(service.get_proposer_for_slot(Slot(3)).is_none()); - assert!(service.get_proposer_for_slot(Slot(4)).is_none()); - assert!(service.get_proposer_for_slot(Slot(5)).is_none()); - } - - #[test] - fn test_is_assigned() { - let config = ValidatorConfig { - node_id: "test_0".to_string(), - validator_indices: vec![2, 5, 8], - }; - - assert!(config.is_assigned(2)); - assert!(config.is_assigned(5)); - assert!(config.is_assigned(8)); - assert!(!config.is_assigned(0)); - assert!(!config.is_assigned(1)); - assert!(!config.is_assigned(3)); - } -} diff --git a/lean_client/validator/tests/unit_tests.rs b/lean_client/validator/tests/unit_tests.rs new file mode 100644 index 0000000..3678c36 --- /dev/null +++ b/lean_client/validator/tests/unit_tests.rs @@ -0,0 +1,38 @@ +use containers::Slot; +use validator::{ValidatorConfig, ValidatorService}; + +#[test] +fn test_proposer_selection() { + let config = ValidatorConfig { + node_id: "test_0".to_string(), + validator_indices: vec![2], + }; + let service = ValidatorService::new(config, 4); + + // Validator 2 should propose at slots 2, 6, 10, ... + assert!(service.get_proposer_for_slot(Slot(2)).is_some()); + assert!(service.get_proposer_for_slot(Slot(6)).is_some()); + assert!(service.get_proposer_for_slot(Slot(10)).is_some()); + + // Validator 2 should NOT propose at slots 0, 1, 3, 4, 5, ... + assert!(service.get_proposer_for_slot(Slot(0)).is_none()); + assert!(service.get_proposer_for_slot(Slot(1)).is_none()); + assert!(service.get_proposer_for_slot(Slot(3)).is_none()); + assert!(service.get_proposer_for_slot(Slot(4)).is_none()); + assert!(service.get_proposer_for_slot(Slot(5)).is_none()); +} + +#[test] +fn test_is_assigned() { + let config = ValidatorConfig { + node_id: "test_0".to_string(), + validator_indices: vec![2, 5, 8], + }; + + assert!(config.is_assigned(2)); + assert!(config.is_assigned(5)); + assert!(config.is_assigned(8)); + assert!(!config.is_assigned(0)); + assert!(!config.is_assigned(1)); + assert!(!config.is_assigned(3)); +} From 3cffa10c432b4fa1b4df846a14ac9f0c05bcdbb9 Mon Sep 17 00:00:00 2001 From: Nojus Sandovas Date: Fri, 23 Jan 2026 22:15:34 +0200 Subject: [PATCH 17/48] Added back comment for unit tests --- lean_client/validator/tests/unit_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lean_client/validator/tests/unit_tests.rs b/lean_client/validator/tests/unit_tests.rs index 3678c36..2da822d 100644 --- a/lean_client/validator/tests/unit_tests.rs +++ b/lean_client/validator/tests/unit_tests.rs @@ -1,3 +1,4 @@ +// AI Generated tests use containers::Slot; use validator::{ValidatorConfig, ValidatorService}; From b9bf511b2b85fe6648acaccae20fd91041d3dce2 Mon Sep 17 00:00:00 2001 From: Nojus Sandovas Date: Sun, 25 Jan 2026 20:31:01 +0200 Subject: [PATCH 18/48] Implemented changes to sync the client with leanSpec devnet-2 --- lean_client/Cargo.lock | 776 +++++++++--------- lean_client/containers/Cargo.toml | 6 +- lean_client/containers/src/attestation.rs | 94 ++- lean_client/containers/src/block.rs | 35 +- lean_client/fork_choice/src/handlers.rs | 210 +++-- lean_client/fork_choice/src/store.rs | 113 +-- .../tests/fork_choice_test_vectors.rs | 19 +- .../tests/unit_tests/fork_choice.rs | 14 +- .../fork_choice/tests/unit_tests/votes.rs | 107 +-- lean_client/networking/src/enr_ext.rs | 4 + .../networking/src/gossipsub/config.rs | 4 +- lean_client/networking/src/req_resp.rs | 2 +- lean_client/networking/src/types.rs | 62 +- lean_client/src/main.rs | 12 +- lean_client/validator/src/lib.rs | 37 +- 15 files changed, 796 insertions(+), 699 deletions(-) diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index 970d374..4aaaef3 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -61,15 +61,22 @@ dependencies = [ [[package]] name = "air" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ "multilinear-toolkit", - "p3-air", "p3-util 0.3.0", "tracing", "utils", ] +[[package]] +name = "air" +version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git?branch=lean-vm-simple#e06cba2e214879c00c7fbc0e5b12908ddfcba588" +dependencies = [ + "p3-field 0.3.0", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -89,7 +96,7 @@ dependencies = [ "derive_more", "foldhash 0.2.0", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.12.1", "itoa", "k256", "keccak-asm", @@ -191,7 +198,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arithmetic" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" +source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" dependencies = [ "easy-ext", "typenum", @@ -261,7 +268,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote 1.0.43", + "quote 1.0.42", "syn 1.0.109", ] @@ -271,7 +278,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.43", + "quote 1.0.42", "syn 1.0.109", ] @@ -281,8 +288,8 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ - "quote 1.0.43", - "syn 2.0.114", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -293,7 +300,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote 1.0.43", + "quote 1.0.42", "syn 1.0.109", ] @@ -305,8 +312,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -318,9 +325,9 @@ checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -420,9 +427,9 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "synstructure 0.13.2", ] @@ -432,9 +439,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -479,9 +486,9 @@ version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -534,9 +541,9 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -548,7 +555,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backend" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git?branch=lean-vm-simple#e06cba2e214879c00c7fbc0e5b12908ddfcba588" dependencies = [ "fiat-shamir", "itertools 0.14.0", @@ -589,9 +596,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bincode" @@ -709,9 +716,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.52" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "shlex", @@ -782,9 +789,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -792,9 +799,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -809,16 +816,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" @@ -883,18 +890,18 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "unicode-xid 0.2.6", ] [[package]] name = "constraints-folder" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git?branch=lean-vm-simple#e06cba2e214879c00c7fbc0e5b12908ddfcba588" dependencies = [ + "air 0.3.0", "fiat-shamir", - "p3-air", "p3-field 0.3.0", ] @@ -905,9 +912,10 @@ dependencies = [ "alloy-primitives", "anyhow", "env-config", + "ethereum_ssz", "hex", "lean-multisig", - "leansig", + "leansig 0.1.0 (git+https://github.com/leanEthereum/leanSig?rev=73bedc26ed961b110df7ac2e234dc11361a4bf25)", "pretty_assertions", "rstest", "serde", @@ -1063,9 +1071,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1086,10 +1094,10 @@ checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "strsim", - "syn 2.0.114", + "syn 2.0.111", ] [[package]] @@ -1099,8 +1107,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", - "quote 1.0.43", - "syn 2.0.114", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1119,15 +1127,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-encoding-macro" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1135,12 +1143,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.17" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.114", + "syn 2.0.111", ] [[package]] @@ -1194,8 +1202,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -1215,10 +1223,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.111", "unicode-xid 0.2.6", ] @@ -1286,16 +1294,16 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "dtoa" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dyn-clone" @@ -1355,9 +1363,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1411,9 +1419,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ "heck", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1431,9 +1439,9 @@ version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1579,7 +1587,7 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fiat-shamir" version = "0.1.0" -source = "git+https://github.com/leanEthereum/fiat-shamir.git#bcf23c766f2e930acf11e68777449483a55af077" +source = "git+https://github.com/leanEthereum/fiat-shamir.git?branch=lean-vm-simple#9d4dc22f06cfa65f15bf5f1b07912a64c7feff0f" dependencies = [ "p3-challenger 0.3.0", "p3-field 0.3.0", @@ -1589,9 +1597,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.7" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fixed-hash" @@ -1725,9 +1733,9 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1791,9 +1799,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -1845,9 +1853,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -1855,7 +1863,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.13.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -1902,7 +1910,7 @@ dependencies = [ [[package]] name = "hashing" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" +source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" dependencies = [ "ethereum-types", "generic-array", @@ -2318,9 +2326,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -2336,9 +2344,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -2417,9 +2425,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" @@ -2473,15 +2481,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lean-multisig" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ "clap", + "lean_vm", "multilinear-toolkit", "p3-koala-bear 0.3.0", - "poseidon_circuit", + "rand 0.9.2", "rec_aggregation", "whir-p3", - "xmss", ] [[package]] @@ -2504,13 +2512,12 @@ dependencies = [ [[package]] name = "lean_compiler" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ - "air", + "air 0.1.0", "lean_vm", "lookup", "multilinear-toolkit", - "p3-air", "p3-challenger 0.3.0", "p3-koala-bear 0.3.0", "p3-poseidon2 0.3.0", @@ -2523,21 +2530,19 @@ dependencies = [ "tracing", "utils", "whir-p3", - "xmss", ] [[package]] name = "lean_prover" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ - "air", + "air 0.1.0", "itertools 0.14.0", "lean_compiler", "lean_vm", "lookup", "multilinear-toolkit", - "p3-air", "p3-challenger 0.3.0", "p3-koala-bear 0.3.0", "p3-poseidon2 0.3.0", @@ -2545,29 +2550,26 @@ dependencies = [ "p3-util 0.3.0", "pest", "pest_derive", - "poseidon_circuit", "rand 0.9.2", "sub_protocols", "tracing", "utils", "whir-p3", "witness_generation", - "xmss", ] [[package]] name = "lean_vm" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ - "air", + "air 0.1.0", "colored", "derive_more", "itertools 0.14.0", "lookup", "multilinear-toolkit", "num_enum", - "p3-air", "p3-challenger 0.3.0", "p3-koala-bear 0.3.0", "p3-poseidon2 0.3.0", @@ -2576,19 +2578,37 @@ dependencies = [ "pest", "pest_derive", "rand 0.9.2", - "strum", "sub_protocols", "thiserror 2.0.17", "tracing", "utils", "whir-p3", - "xmss", ] [[package]] name = "leansig" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanSig?branch=main#ae12a5feb25d917c42b6466444ebd56ec115a629" +source = "git+https://github.com/leanEthereum/leanSig?branch=main#73bedc26ed961b110df7ac2e234dc11361a4bf25" +dependencies = [ + "dashmap", + "ethereum_ssz", + "num-bigint", + "num-traits", + "p3-baby-bear 0.4.1", + "p3-field 0.4.1", + "p3-koala-bear 0.4.1", + "p3-symmetric 0.4.1", + "rand 0.9.2", + "rayon", + "serde", + "sha3", + "thiserror 2.0.17", +] + +[[package]] +name = "leansig" +version = "0.1.0" +source = "git+https://github.com/leanEthereum/leanSig?rev=73bedc26ed961b110df7ac2e234dc11361a4bf25#73bedc26ed961b110df7ac2e234dc11361a4bf25" dependencies = [ "dashmap", "ethereum_ssz", @@ -2612,9 +2632,9 @@ source = "git+https://github.com/0xPolygonHermez/zisk.git?tag=v0.13.0#ea1ed4c518 [[package]] name = "libc" -version = "0.2.180" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libm" @@ -2632,10 +2652,10 @@ dependencies = [ "either", "futures", "futures-timer", - "getrandom 0.2.17", + "getrandom 0.2.16", "libp2p-allow-block-list", "libp2p-connection-limits", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-dns", "libp2p-gossipsub", "libp2p-identify", @@ -2661,7 +2681,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16ccf824ee859ca83df301e1c0205270206223fd4b1f2e512a693e1912a8f4a" dependencies = [ - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-swarm", ] @@ -2672,7 +2692,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18b8b607cf3bfa2f8c57db9c7d8569a315d5cc0a282e6bfd5ebfc0a9840b2a0" dependencies = [ - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-swarm", ] @@ -2707,9 +2727,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.43.2" +version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "249128cd37a2199aff30a7675dffa51caf073b51aa612d2f544b19932b9aebca" +checksum = "4d28e2d2def7c344170f5c6450c0dbe3dfef655610dbfde2f6ac28a527abbe36" dependencies = [ "either", "fnv", @@ -2739,7 +2759,7 @@ dependencies = [ "async-trait", "futures", "hickory-resolver", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "parking_lot", "smallvec", @@ -2761,10 +2781,10 @@ dependencies = [ "fnv", "futures", "futures-timer", - "getrandom 0.2.17", + "getrandom 0.2.16", "hashlink", "hex_fmt", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-swarm", "quick-protobuf", @@ -2787,7 +2807,7 @@ dependencies = [ "futures", "futures-bounded", "futures-timer", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-swarm", "quick-protobuf", @@ -2844,7 +2864,7 @@ dependencies = [ "futures", "hickory-proto", "if-watch", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-swarm", "rand 0.8.5", @@ -2861,7 +2881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "805a555148522cb3414493a5153451910cb1a146c53ffbf4385708349baf62b7" dependencies = [ "futures", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-gossipsub", "libp2p-identify", "libp2p-identity 0.2.13", @@ -2898,7 +2918,7 @@ dependencies = [ "asynchronous-codec 0.7.0", "bytes", "futures", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "multiaddr 0.18.2", "multihash 0.19.3", @@ -2921,7 +2941,7 @@ dependencies = [ "futures", "futures-timer", "if-watch", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-tls", "quinn", @@ -2943,7 +2963,7 @@ dependencies = [ "async-trait", "futures", "futures-bounded", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-swarm", "rand 0.8.5", @@ -2961,7 +2981,7 @@ dependencies = [ "fnv", "futures", "futures-timer", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "libp2p-swarm-derive", "lru", @@ -2980,8 +3000,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd297cf53f0cb3dee4d2620bb319ae47ef27c702684309f682bdb7e55a18ae9c" dependencies = [ "heck", - "quote 1.0.43", - "syn 2.0.114", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -2994,7 +3014,7 @@ dependencies = [ "futures-timer", "if-watch", "libc", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "socket2 0.5.10", "tokio", "tracing", @@ -3008,7 +3028,7 @@ checksum = "96ff65a82e35375cbc31ebb99cacbbf28cb6c4fefe26bf13756ddcf708d40080" dependencies = [ "futures", "futures-rustls", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-identity 0.2.13", "rcgen", "ring", @@ -3028,7 +3048,7 @@ dependencies = [ "futures", "futures-timer", "igd-next", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "libp2p-swarm", "tokio", "tracing", @@ -3042,7 +3062,7 @@ checksum = "f15df094914eb4af272acf9adaa9e287baa269943f32ea348ba29cfb9bfc60d8" dependencies = [ "either", "futures", - "libp2p-core 0.43.2", + "libp2p-core 0.43.1", "thiserror 2.0.17", "tracing", "yamux 0.12.1", @@ -3079,7 +3099,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lookup" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ "multilinear-toolkit", "p3-challenger 0.3.0", @@ -3112,8 +3132,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -3151,9 +3171,9 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.12" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3dec6bd31b08944e08b58fd99373893a6c17054d6f3ea5006cc894f4f4eee2a" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -3161,6 +3181,7 @@ dependencies = [ "equivalent", "parking_lot", "portable-atomic", + "rustc_version 0.4.1", "smallvec", "tagptr", "uuid", @@ -3251,8 +3272,8 @@ checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro-error", - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "synstructure 0.12.6", ] @@ -3260,8 +3281,9 @@ dependencies = [ [[package]] name = "multilinear-toolkit" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git?branch=lean-vm-simple#e06cba2e214879c00c7fbc0e5b12908ddfcba588" dependencies = [ + "air 0.3.0", "backend", "constraints-folder", "fiat-shamir", @@ -3269,6 +3291,7 @@ dependencies = [ "p3-util 0.3.0", "rayon", "sumcheck", + "tracing", ] [[package]] @@ -3490,9 +3513,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate 3.4.0", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -3526,19 +3549,10 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "p3-air" -version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" -dependencies = [ - "p3-field 0.3.0", - "p3-matrix 0.3.0", -] - [[package]] name = "p3-baby-bear" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "p3-field 0.3.0", "p3-mds 0.3.0", @@ -3565,7 +3579,7 @@ dependencies = [ [[package]] name = "p3-challenger" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "p3-field 0.3.0", "p3-maybe-rayon 0.3.0", @@ -3590,7 +3604,7 @@ dependencies = [ [[package]] name = "p3-commit" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "p3-challenger 0.3.0", @@ -3604,7 +3618,7 @@ dependencies = [ [[package]] name = "p3-dft" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "p3-field 0.3.0", @@ -3631,7 +3645,7 @@ dependencies = [ [[package]] name = "p3-field" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -3661,7 +3675,7 @@ dependencies = [ [[package]] name = "p3-interpolation" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "p3-field 0.3.0", "p3-matrix 0.3.0", @@ -3672,7 +3686,7 @@ dependencies = [ [[package]] name = "p3-koala-bear" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -3701,7 +3715,7 @@ dependencies = [ [[package]] name = "p3-matrix" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "p3-field 0.3.0", @@ -3731,7 +3745,7 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "rayon", ] @@ -3744,7 +3758,7 @@ source = "git+https://github.com/Plonky3/Plonky3.git?rev=d421e32#d421e32d3821174 [[package]] name = "p3-mds" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "p3-dft 0.3.0", "p3-field 0.3.0", @@ -3768,7 +3782,7 @@ dependencies = [ [[package]] name = "p3-merkle-tree" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "p3-commit", @@ -3785,7 +3799,7 @@ dependencies = [ [[package]] name = "p3-monty-31" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -3830,7 +3844,7 @@ dependencies = [ [[package]] name = "p3-poseidon2" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "p3-field 0.3.0", "p3-mds 0.3.0", @@ -3854,7 +3868,7 @@ dependencies = [ [[package]] name = "p3-symmetric" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "itertools 0.14.0", "p3-field 0.3.0", @@ -3874,7 +3888,7 @@ dependencies = [ [[package]] name = "p3-util" version = "0.3.0" -source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-multisig#1db9df28abd6db586eaa891af2416d94d1b026ae" +source = "git+https://github.com/TomWambsgans/Plonky3.git?branch=lean-vm-simple#4897086b6f460b969dc0baad5c4dff91a4eb1d67" dependencies = [ "rayon", "serde", @@ -3911,9 +3925,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ "proc-macro-crate 3.4.0", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -3969,9 +3983,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" dependencies = [ "memchr", "ucd-trie", @@ -3979,9 +3993,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" dependencies = [ "pest", "pest_generator", @@ -3989,22 +4003,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" dependencies = [ "pest", "sha2 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4025,9 +4039,9 @@ version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -4091,25 +4105,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" - -[[package]] -name = "poseidon_circuit" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" -dependencies = [ - "multilinear-toolkit", - "p3-koala-bear 0.3.0", - "p3-monty-31 0.3.0", - "p3-poseidon2 0.3.0", - "rand 0.9.2", - "sub_protocols", - "tracing", - "utils", - "whir-p3", -] +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "potential_utf" @@ -4184,8 +4182,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "version_check", ] @@ -4196,8 +4194,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "version_check", ] @@ -4212,9 +4210,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -4237,9 +4235,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -4356,11 +4354,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.43" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ - "proc-macro2 1.0.105", + "proc-macro2 1.0.103", ] [[package]] @@ -4393,7 +4391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.4", + "rand_core 0.9.3", "serde", ] @@ -4414,7 +4412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.4", + "rand_core 0.9.3", ] [[package]] @@ -4423,14 +4421,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.16", ] [[package]] name = "rand_core" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.4", "serde", @@ -4442,14 +4440,14 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.4", + "rand_core 0.9.3", ] [[package]] name = "rapidhash" -version = "4.2.1" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +checksum = "d8e65c75143ce5d47c55b510297eeb1182f3c739b6043c537670e9fc18612dae" dependencies = [ "rustversion", ] @@ -4490,16 +4488,18 @@ dependencies = [ [[package]] name = "rec_aggregation" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ - "air", + "air 0.1.0", "bincode", + "ethereum_ssz", + "hex", "lean_compiler", "lean_prover", "lean_vm", + "leansig 0.1.0 (git+https://github.com/leanEthereum/leanSig?rev=73bedc26ed961b110df7ac2e234dc11361a4bf25)", "lookup", "multilinear-toolkit", - "p3-air", "p3-challenger 0.3.0", "p3-koala-bear 0.3.0", "p3-poseidon2 0.3.0", @@ -4512,7 +4512,6 @@ dependencies = [ "tracing", "utils", "whir-p3", - "xmss", ] [[package]] @@ -4539,9 +4538,9 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -4609,7 +4608,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.17", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -4645,12 +4644,12 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.111", "unicode-ident", ] @@ -4674,9 +4673,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.17.2" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -4747,9 +4746,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags 2.10.0", "errno", @@ -4760,9 +4759,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", "ring", @@ -4774,9 +4773,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ "web-time", "zeroize", @@ -4835,9 +4834,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schemars" @@ -4853,9 +4852,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -4932,29 +4931,29 @@ version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.12.1", "itoa", "memchr", + "ryu", "serde", "serde_core", - "zmij", ] [[package]] name = "serde_utils" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" +source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" dependencies = [ "const-hex", "generic-array", @@ -4977,9 +4976,9 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.2.0", + "schemars 1.1.0", "serde_core", "serde_json", "serde_with_macros", @@ -4993,9 +4992,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5004,7 +5003,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -5070,11 +5069,10 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.8" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ - "errno", "libc", ] @@ -5165,7 +5163,7 @@ dependencies = [ [[package]] name = "ssz" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" +source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" dependencies = [ "arithmetic", "bit_field", @@ -5197,15 +5195,15 @@ dependencies = [ [[package]] name = "ssz_derive" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" +source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" dependencies = [ "darling", "easy-ext", "itertools 0.14.0", "proc-macro-crate 3.4.0", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5223,7 +5221,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "std_ext" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" +source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" dependencies = [ "easy-ext", "triomphe", @@ -5241,31 +5239,10 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", -] - [[package]] name = "sub_protocols" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ "derive_more", "lookup", @@ -5285,12 +5262,12 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sumcheck" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#62766141561550c3540f9f644085fec53d721f16" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git?branch=lean-vm-simple#e06cba2e214879c00c7fbc0e5b12908ddfcba588" dependencies = [ + "air 0.3.0", "backend", "constraints-folder", "fiat-shamir", - "p3-air", "p3-field 0.3.0", "p3-util 0.3.0", "rayon", @@ -5313,19 +5290,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.114" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "unicode-ident", ] @@ -5335,8 +5312,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "unicode-xid 0.2.6", ] @@ -5347,9 +5324,9 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5387,9 +5364,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", "getrandom 0.3.4", @@ -5434,9 +5411,9 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5445,9 +5422,9 @@ version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5526,9 +5503,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.49.0" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ "bytes", "libc", @@ -5547,16 +5524,16 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -5577,20 +5554,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.12.1", "toml_datetime", "toml_parser", "winnow", @@ -5598,9 +5575,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] @@ -5613,9 +5590,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.44" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -5629,21 +5606,33 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.36" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", ] +[[package]] +name = "tracing-forest" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3298fe855716711a00474eceb89cc7dc254bbe67f6bc4afafdeec5f0c538771c" +dependencies = [ + "smallvec", + "thiserror 2.0.17", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-forest" version = "0.3.0" @@ -5715,7 +5704,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "try_from_iterator" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#8ac065da176067bc4eb8b79ebfc48a6433f52499" +source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" [[package]] name = "typenum" @@ -5823,9 +5812,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.8" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -5848,17 +5837,16 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utils" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ "multilinear-toolkit", - "p3-air", "p3-challenger 0.3.0", "p3-koala-bear 0.3.0", "p3-poseidon2 0.3.0", "p3-symmetric 0.3.0", "p3-util 0.3.0", "tracing", - "tracing-forest", + "tracing-forest 0.3.0", "tracing-subscriber", ] @@ -5880,7 +5868,7 @@ dependencies = [ "containers", "env-config", "fork-choice", - "leansig", + "leansig 0.1.0 (git+https://github.com/leanEthereum/leanSig?branch=main)", "serde_yaml", "tracing", "typenum", @@ -5956,7 +5944,7 @@ version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ - "quote 1.0.43", + "quote 1.0.42", "wasm-bindgen-macro-support", ] @@ -5967,9 +5955,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -5995,7 +5983,7 @@ dependencies = [ [[package]] name = "whir-p3" version = "0.1.0" -source = "git+https://github.com/TomWambsgans/whir-p3?branch=lean-multisig#04fb1c1f2e3bbd14e6e4aee32621656eb3f3949f" +source = "git+https://github.com/TomWambsgans/whir-p3?branch=lean-vm-simple#f74bc197415a597b1ca316a4ee207f43c8adee85" dependencies = [ "itertools 0.14.0", "multilinear-toolkit", @@ -6015,7 +6003,7 @@ dependencies = [ "rayon", "thiserror 2.0.17", "tracing", - "tracing-forest", + "tracing-forest 0.2.0", "tracing-subscriber", ] @@ -6086,9 +6074,9 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -6097,9 +6085,9 @@ version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -6394,15 +6382,14 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "witness_generation" version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" +source = "git+https://github.com/leanEthereum/leanMultisig?rev=e4474138487eeb1ed7c2e1013674fe80ac9f3165#e4474138487eeb1ed7c2e1013674fe80ac9f3165" dependencies = [ - "air", + "air 0.1.0", "derive_more", "lean_compiler", "lean_vm", "lookup", "multilinear-toolkit", - "p3-air", "p3-challenger 0.3.0", "p3-koala-bear 0.3.0", "p3-monty-31 0.3.0", @@ -6411,13 +6398,11 @@ dependencies = [ "p3-util 0.3.0", "pest", "pest_derive", - "poseidon_circuit", "rand 0.9.2", "sub_protocols", "tracing", "utils", "whir-p3", - "xmss", ] [[package]] @@ -6479,19 +6464,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "xmss" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanMultisig?branch=main#72c27460314770dee435adf80494b2d684d3959b" -dependencies = [ - "multilinear-toolkit", - "p3-koala-bear 0.3.0", - "p3-util 0.3.0", - "rand 0.9.2", - "sha3", - "utils", -] - [[package]] name = "yamux" version = "0.12.1" @@ -6555,30 +6527,30 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "synstructure 0.13.2", ] [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -6596,9 +6568,9 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "synstructure 0.13.2", ] @@ -6613,13 +6585,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -6650,9 +6622,9 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ - "proc-macro2 1.0.105", - "quote 1.0.43", - "syn 2.0.114", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -6661,7 +6633,7 @@ version = "0.13.0" source = "git+https://github.com/0xPolygonHermez/zisk.git?tag=v0.13.0#ea1ed4c518992a170fc59ec19f1228eb4829a9e1" dependencies = [ "cfg-if", - "getrandom 0.2.17", + "getrandom 0.2.16", "lazy_static", "lib-c", "num-bigint", @@ -6670,9 +6642,3 @@ dependencies = [ "static_assertions", "tiny-keccak", ] - -[[package]] -name = "zmij" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec" diff --git a/lean_client/containers/Cargo.toml b/lean_client/containers/Cargo.toml index ad85010..1b8e8e5 100644 --- a/lean_client/containers/Cargo.toml +++ b/lean_client/containers/Cargo.toml @@ -21,10 +21,12 @@ serde_json = "1.0" serde_yaml = "0.9" hex = "0.4.3" sha2 = "0.10" -leansig = { git = "https://github.com/leanEthereum/leanSig", branch = "main" } -lean-multisig = { git = "https://github.com/leanEthereum/leanMultisig", branch = "main" } +leansig = { git = "https://github.com/leanEthereum/leanSig", rev = "73bedc26ed961b110df7ac2e234dc11361a4bf25" } +lean-multisig = { git = "https://github.com/leanEthereum/leanMultisig", rev = "e4474138487eeb1ed7c2e1013674fe80ac9f3165" } anyhow = "1.0.100" alloy-primitives = "1.5.2" +# ethereum_ssz for lean-multisig types (aliased to avoid conflict with grandine ssz) +eth_ssz = { package = "ethereum_ssz", version = "0.10.0" } [dev-dependencies] rstest = "0.18" diff --git a/lean_client/containers/src/attestation.rs b/lean_client/containers/src/attestation.rs index ba0596c..fb45e0c 100644 --- a/lean_client/containers/src/attestation.rs +++ b/lean_client/containers/src/attestation.rs @@ -1,5 +1,5 @@ use crate::{Checkpoint, Slot, Uint64}; -use leansig::serialization::Serializable; +use anyhow::anyhow; use serde::{Deserialize, Serialize}; use ssz::BitList; use ssz::ByteVector; @@ -70,18 +70,18 @@ impl MultisigAggregatedSignature { /// Uses lean-multisig zkVM to combine multiple signatures into a compact proof. /// /// # Arguments - /// * `public_keys` - Public keys of the signers + /// * `public_keys` - Slice of validator public keys /// * `signatures` - Individual XMSS signatures to aggregate - /// * `message` - The 32-byte message that was signed (as 8 field elements) + /// * `message` - The 32-byte message that was signed /// * `epoch` - The epoch/slot in which signatures were created /// /// # Returns /// Aggregated signature proof, or error if aggregation fails. pub fn aggregate( - public_keys: &[lean_multisig::XmssPublicKey], - signatures: &[lean_multisig::XmssSignature], - message: [lean_multisig::F; 8], - epoch: u64, + public_keys: &[crate::public_key::PublicKey], + signatures: &[Signature], + message: &[u8; 32], + epoch: u32, ) -> Result { if public_keys.is_empty() { return Err(AggregationError::EmptyInput); @@ -90,10 +90,30 @@ impl MultisigAggregatedSignature { return Err(AggregationError::MismatchedLengths); } - let proof_bytes = - lean_multisig::xmss_aggregate_signatures(public_keys, signatures, message, epoch) + // Convert to lean-multisig types + let lean_pks: Vec<_> = public_keys + .iter() + .map(|pk| pk.as_lean_sig()) + .collect::, _>>() + .map_err(|_| AggregationError::AggregationFailed)?; + + let lean_sigs: Vec<_> = signatures + .iter() + .map(|sig| { + // Convert ByteVector to crate::signature::Signature then to lean-sig + let sig_struct = crate::signature::Signature::from(sig.as_bytes()); + sig_struct.as_lean_sig() + }) + .collect::, _>>() + .map_err(|_| AggregationError::AggregationFailed)?; + + let aggregate_sig = + lean_multisig::xmss_aggregate_signatures(&lean_pks, &lean_sigs, message, epoch) .map_err(|_| AggregationError::AggregationFailed)?; + // Serialize the aggregate signature using ethereum_ssz (aliased as eth_ssz) + use eth_ssz::Encode; + let proof_bytes = aggregate_sig.as_ssz_bytes(); Self::new(proof_bytes) } @@ -106,23 +126,32 @@ impl MultisigAggregatedSignature { /// `Ok(())` if the proof is valid, `Err` with the proof error otherwise. pub fn verify( &self, - public_keys: &[lean_multisig::XmssPublicKey], - message: [lean_multisig::F; 8], - epoch: u64, + public_keys: &[crate::public_key::PublicKey], + message: &[u8; 32], + epoch: u32, ) -> Result<(), AggregationError> { - lean_multisig::xmss_verify_aggregated_signatures( - public_keys, - message, - self.0.as_bytes(), - epoch, - ) - .map_err(|_| AggregationError::VerificationFailed) + // Use ethereum_ssz (aliased as eth_ssz) for decoding + use eth_ssz::Decode; + + // Decode the aggregated signature from SSZ bytes + let aggregate_sig = + lean_multisig::Devnet2XmssAggregateSignature::from_ssz_bytes(self.0.as_bytes()) + .map_err(|_| AggregationError::VerificationFailed)?; + + // Convert public keys to lean-multisig format + let lean_pks: Vec<_> = public_keys + .iter() + .map(|pk| pk.as_lean_sig()) + .collect::, _>>() + .map_err(|_| AggregationError::VerificationFailed)?; + + lean_multisig::xmss_verify_aggregated_signatures(&lean_pks, message, &aggregate_sig, epoch) + .map_err(|_| AggregationError::VerificationFailed) } /// Verify the aggregated payload against validators and message. /// - /// This is a convenience method that extracts public keys from validators - /// and converts the message bytes to the field element format expected by lean-multisig. + /// This is a convenience method that extracts public keys from validators. /// /// # Arguments /// * `validators` - Slice of validator references to extract public keys from @@ -135,28 +164,13 @@ impl MultisigAggregatedSignature { &self, validators: &[&crate::validator::Validator], message: &[u8; 32], - epoch: u64, + epoch: u32, ) -> Result<(), AggregationError> { // Extract public keys from validators - let mut public_keys = Vec::new(); - for validator in validators { - // Convert PublicKey to lean_multisig::XmssPublicKey - let lean_sig_pk = validator - .pubkey - .as_lean_sig() - .map_err(|_| AggregationError::VerificationFailed)?; - let pk_bytes = lean_sig_pk.to_bytes(); - // TODO: Implement proper conversion from PublicKey bytes to lean_multisig::XmssPublicKey - // Once lean-multisig API is clarified, convert pk_bytes to XmssPublicKey - todo!("Convert PublicKey to lean_multisig::XmssPublicKey and implement message field conversion"); - } - - // Convert 32-byte message to 8 field elements - // TODO: Implement proper conversion from 32 bytes to 8 field elements - let message_fields = todo!("Convert 32-byte message to [lean_multisig::F; 8]"); + let public_keys: Vec<_> = validators.iter().map(|v| v.pubkey).collect(); - // Call verify with extracted keys and converted message - self.verify(&public_keys, message_fields, epoch) + // Call verify with extracted keys + self.verify(&public_keys, message, epoch) } } diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index 52d6d59..d9de1fb 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -158,24 +158,24 @@ impl SignedBlockWithAttestation { ); } - // let attestation_data_root: [u8; 32] = - // hash_tree_root(&aggregated_attestation.data).0.into(); + let attestation_data_root: [u8; 32] = + hash_tree_root(&aggregated_attestation.data).0.into(); // Verify the lean-multisig aggregated proof for this attestation // // The proof verifies that all validators in aggregation_bits signed // the same attestation_data_root at the given epoch (slot). - // TODO - // aggregated_signature_proof - // .verify_aggregated_payload( - // &validator_ids - // .iter() - // .map(|vid| validators.get(*vid).expect("validator must exist")) - // .collect::>(), - // &attestation_data_root, - // aggregated_attestation.data.slot.0, - // ) - // .expect("Attestation aggregated signature verification failed"); + _aggregated_signature_proof + .proof_data + .verify_aggregated_payload( + &validator_ids + .iter() + .map(|vid| validators.get(*vid).expect("validator must exist")) + .collect::>(), + &attestation_data_root, + aggregated_attestation.data.slot.0 as u32, + ) + .expect("Attestation aggregated signature verification failed"); } // Verify the proposer attestation signature (outside the attestation loop) @@ -214,11 +214,12 @@ pub fn verify_xmss_signature( signature: &Signature, ) -> bool { let epoch = slot.0 as u32; - let signature = crate::signature::Signature::from(signature.as_bytes()); - signature - .verify(&public_key, epoch, message_bytes) - .unwrap_or_else(|_| false) + // Create Signature from the raw bytes + let sig = crate::signature::Signature::from(signature.as_bytes()); + + sig.verify(&public_key, epoch, message_bytes) + .unwrap_or(false) } #[cfg(not(feature = "xmss-verify"))] diff --git a/lean_client/fork_choice/src/handlers.rs b/lean_client/fork_choice/src/handlers.rs index 3054052..5888942 100644 --- a/lean_client/fork_choice/src/handlers.rs +++ b/lean_client/fork_choice/src/handlers.rs @@ -1,4 +1,5 @@ use crate::store::*; +use containers::attestation::AttestationData; use containers::SignatureKey; use containers::{ attestation::SignedAttestation, block::SignedBlockWithAttestation, Bytes32, ValidatorIndex, @@ -20,6 +21,106 @@ pub fn on_tick(store: &mut Store, time: u64, has_proposal: bool) { } } +/// 1. The blocks voted for must exist in our store. +/// 2. A vote cannot span backwards in time (source > target). +/// 3. A vote cannot be for a future slot. +/// 4. Checkpoint slots must match block slots. +fn validate_attestation_data(store: &Store, data: &AttestationData) -> Result<(), String> { + // Cannot count a vote if we haven't seen the blocks involved + if !store.blocks.contains_key(&data.source.root) { + return Err(format!( + "Unknown source block: {:?}", + &data.source.root.0.as_bytes()[..8] + )); + } + if !store.blocks.contains_key(&data.target.root) { + return Err(format!( + "Unknown target block: {:?}", + &data.target.root.0.as_bytes()[..8] + )); + } + if !store.blocks.contains_key(&data.head.root) { + return Err(format!( + "Unknown head block: {:?}", + &data.head.root.0.as_bytes()[..8] + )); + } + + // Source must be older than Target. + if data.source.slot > data.target.slot { + return Err(format!( + "Source checkpoint slot {} must not exceed target slot {}", + data.source.slot.0, data.target.slot.0 + )); + } + + // Validate checkpoint slots match block slots + // Per devnet-2, store.blocks now contains Block (not SignedBlockWithAttestation) + let source_block = &store.blocks[&data.source.root]; + let target_block = &store.blocks[&data.target.root]; + + if source_block.slot != data.source.slot { + return Err(format!( + "Source checkpoint slot mismatch: checkpoint {} vs block {}", + data.source.slot.0, source_block.slot.0 + )); + } + if target_block.slot != data.target.slot { + return Err(format!( + "Target checkpoint slot mismatch: checkpoint {} vs block {}", + data.target.slot.0, target_block.slot.0 + )); + } + + // Validate attestation is not too far in the future + // We allow a small margin for clock disparity (1 slot), but no further. + let current_slot = store.time / INTERVALS_PER_SLOT; + if data.slot.0 > current_slot + 1 { + return Err(format!( + "Attestation too far in future: attestation slot {} > current slot {} + 1", + data.slot.0, current_slot + )); + } + + Ok(()) +} + +/// Process a signed attestation received via gossip network +/// +/// 1. Validates the attestation data +/// 2. Stores the signature in the gossip signature map +/// 3. Processes the attestation data via on_attestation +/// +#[inline] +pub fn on_gossip_attestation( + store: &mut Store, + signed_attestation: SignedAttestation, +) -> Result<(), String> { + let validator_id = ValidatorIndex(signed_attestation.validator_id); + let attestation_data = signed_attestation.message.clone(); + + // Validate the attestation data first + validate_attestation_data(store, &attestation_data)?; + + // Store signature for later lookup during block building + let data_root = attestation_data.data_root_bytes(); + let sig_key = SignatureKey::new(signed_attestation.validator_id, data_root); + store + .gossip_signatures + .insert(sig_key, signed_attestation.signature); + + // Process the attestation data (not from block) + on_attestation_internal(store, validator_id, attestation_data, false) +} + +/// Process an attestation and place it into the correct attestation stage +/// +/// Attestation processing logic that updates the attestation +/// maps used for fork choice. Per devnet-2, we store AttestationData only (not signatures). +/// +/// Attestations can come from: +/// - a block body (on-chain, `is_from_block=True`), or +/// - the gossip network (off-chain, `is_from_block=False`). #[inline] pub fn on_attestation( store: &mut Store, @@ -27,68 +128,72 @@ pub fn on_attestation( is_from_block: bool, ) -> Result<(), String> { let validator_id = ValidatorIndex(signed_attestation.validator_id); - let attestation_slot = signed_attestation.message.slot; - let source_slot = signed_attestation.message.source.slot; - let target_slot = signed_attestation.message.target.slot; + let attestation_data = signed_attestation.message.clone(); - // Validate attestation is not from future - let curr_slot = store.time / INTERVALS_PER_SLOT; - if attestation_slot.0 > curr_slot { - return Err(format!( - "Err: (Fork-choice::Handlers::OnAttestation) Attestation for slot {} has not yet occurred, out of sync. (CURRENT SLOT NUMBER: {})", - attestation_slot.0, curr_slot - )); - } + // Validate attestation data + validate_attestation_data(store, &attestation_data)?; - // Validate source slot does not exceed target slot (per leanSpec validate_attestation) - if source_slot > target_slot { - return Err(format!( - "Err: (Fork-choice::Handlers::OnAttestation) Source slot {} exceeds target slot {}", - source_slot.0, target_slot.0 - )); + if !is_from_block { + // Store signature for later aggregation during block building + let data_root = attestation_data.data_root_bytes(); + let sig_key = SignatureKey::new(signed_attestation.validator_id, data_root); + store + .gossip_signatures + .insert(sig_key, signed_attestation.signature); } + on_attestation_internal(store, validator_id, attestation_data, is_from_block) +} + +/// Internal attestation processing - stores AttestationData +fn on_attestation_internal( + store: &mut Store, + validator_id: ValidatorIndex, + attestation_data: AttestationData, + is_from_block: bool, +) -> Result<(), String> { + let attestation_slot = attestation_data.slot; + if is_from_block { - // On-chain attestation processing - immediately becomes "known" + // On-chain attestation processing if store .latest_known_attestations .get(&validator_id) - .map_or(true, |existing| existing.message.slot < attestation_slot) + .map_or(true, |existing| existing.slot < attestation_slot) { store .latest_known_attestations - .insert(validator_id, signed_attestation.clone()); + .insert(validator_id, attestation_data); } // Remove from new attestations if superseded if let Some(existing_new) = store.latest_new_attestations.get(&validator_id) { - if existing_new.message.slot <= attestation_slot { + if existing_new.slot <= attestation_slot { store.latest_new_attestations.remove(&validator_id); } } } else { // Network gossip attestation processing - goes to "new" stage - // Store signature for later aggregation during block building - let data_root = signed_attestation.message.data_root_bytes(); - let sig_key = SignatureKey::new(signed_attestation.validator_id, data_root); - store - .gossip_signatures - .insert(sig_key, signed_attestation.signature.clone()); - - // Track attestation for fork choice if store .latest_new_attestations .get(&validator_id) - .map_or(true, |existing| existing.message.slot < attestation_slot) + .map_or(true, |existing| existing.slot < attestation_slot) { store .latest_new_attestations - .insert(validator_id, signed_attestation); + .insert(validator_id, attestation_data); } } Ok(()) } +/// Process a new block and update the forkchoice state +/// +/// 1. Validating the block's parent exists +/// 2. Computing the post-state via the state transition function +/// 3. Processing attestations included in the block body (on-chain) +/// 4. Updating the forkchoice head +/// 5. Processing the proposer's attestation (as if gossiped) pub fn on_block(store: &mut Store, signed_block: SignedBlockWithAttestation) -> Result<(), String> { let block_root = Bytes32(signed_block.message.block.hash_tree_root()); @@ -122,7 +227,7 @@ fn process_block_internal( signed_block: SignedBlockWithAttestation, block_root: Bytes32, ) -> Result<(), String> { - let block = &signed_block.message.block; + let block = signed_block.message.block.clone(); // Get parent state for validation let state = match store.states.get(&block.parent_root) { @@ -137,8 +242,8 @@ fn process_block_internal( // Execute state transition to get post-state let new_state = state.state_transition_with_validation(signed_block.clone(), true, true)?; - // Store block and state - store.blocks.insert(block_root, signed_block.clone()); + // Store block and state, store the plain Block (not SignedBlockWithAttestation) + store.blocks.insert(block_root, block.clone()); store.states.insert(block_root, new_state.clone()); if new_state.latest_justified.slot > store.latest_justified.slot { @@ -150,8 +255,7 @@ fn process_block_internal( // Process block body attestations as on-chain (is_from_block=true) let signatures = &signed_block.signature; - - let aggregated_attestations = &signed_block.message.block.body.attestations; + let aggregated_attestations = &block.body.attestations; let proposer_attestation = &signed_block.message.proposer_attestation; // Store aggregated proofs for future block building @@ -177,7 +281,8 @@ fn process_block_internal( } // Process each aggregated attestation's validators for fork choice - // Note: Signature verification is done in verify_signatures() before on_block() + // Signature verification is done in verify_signatures() before on_block() + // Per Devnet-2, we process attestation data directly (not SignedAttestation) for aggregated_attestation in aggregated_attestations.into_iter() { let validator_ids: Vec = aggregated_attestation .aggregation_bits @@ -190,15 +295,11 @@ fn process_block_internal( // Each validator in the aggregation votes for this attestation data for validator_id in validator_ids { - on_attestation( + on_attestation_internal( store, - SignedAttestation { - validator_id, - message: aggregated_attestation.data.clone(), - // Use a default signature since verification already happened - signature: containers::Signature::default(), - }, - true, + ValidatorIndex(validator_id), + aggregated_attestation.data.clone(), + true, // is_from_block )?; } } @@ -206,15 +307,22 @@ fn process_block_internal( // Update head BEFORE processing proposer attestation update_head(store); - let proposer_signed_attestation = SignedAttestation { - validator_id: proposer_attestation.validator_id.0, - message: proposer_attestation.data.clone(), - signature: signed_block.signature.proposer_signature, - }; + // Store proposer's signature for later block building + let proposer_data_root = proposer_attestation.data.data_root_bytes(); + let proposer_sig_key = + SignatureKey::new(proposer_attestation.validator_id.0, proposer_data_root); + store + .gossip_signatures + .insert(proposer_sig_key, signed_block.signature.proposer_signature); // Process proposer attestation as if received via gossip (is_from_block=false) // This ensures it goes to "new" attestations and doesn't immediately affect fork choice - on_attestation(store, proposer_signed_attestation, false)?; + on_attestation_internal( + store, + ValidatorIndex(proposer_attestation.validator_id.0), + proposer_attestation.data.clone(), + false, // is_from_block + )?; Ok(()) } diff --git a/lean_client/fork_choice/src/store.rs b/lean_client/fork_choice/src/store.rs index 818b57d..07100ba 100644 --- a/lean_client/fork_choice/src/store.rs +++ b/lean_client/fork_choice/src/store.rs @@ -1,27 +1,44 @@ use containers::{ - attestation::SignedAttestation, block::SignedBlockWithAttestation, checkpoint::Checkpoint, - config::Config, state::State, Bytes32, Root, Slot, ValidatorIndex, + attestation::{AttestationData, SignedAttestation}, + block::{Block, SignedBlockWithAttestation}, + checkpoint::Checkpoint, + config::Config, + state::State, + Bytes32, Root, Slot, ValidatorIndex, }; use containers::{AggregatedSignatureProof, Signature, SignatureKey}; use ssz::SszHash; use std::collections::HashMap; + pub type Interval = u64; pub const INTERVALS_PER_SLOT: Interval = 4; pub const SECONDS_PER_SLOT: u64 = 4; pub const SECONDS_PER_INTERVAL: u64 = SECONDS_PER_SLOT / INTERVALS_PER_SLOT; +/// Forkchoice store tracking chain state and validator attestations + #[derive(Debug, Clone, Default)] pub struct Store { pub time: Interval, + pub config: Config, + pub head: Root, + pub safe_target: Root, + pub latest_justified: Checkpoint, + pub latest_finalized: Checkpoint, - pub blocks: HashMap, + + pub blocks: HashMap, + pub states: HashMap, - pub latest_known_attestations: HashMap, - pub latest_new_attestations: HashMap, + + pub latest_known_attestations: HashMap, + + pub latest_new_attestations: HashMap, + pub blocks_queue: HashMap>, pub gossip_signatures: HashMap, @@ -29,13 +46,16 @@ pub struct Store { pub aggregated_payloads: HashMap>, } +/// Initialize forkchoice store from an anchor state and block pub fn get_forkchoice_store( anchor_state: State, anchor_block: SignedBlockWithAttestation, config: Config, ) -> Store { - let block_root = Bytes32(anchor_block.message.block.hash_tree_root()); - let block_slot = anchor_block.message.block.slot; + // Extract the plain Block from the signed block + let block = anchor_block.message.block.clone(); + let block_root = Bytes32(block.hash_tree_root()); + let block_slot = block.slot; let latest_justified = if anchor_state.latest_justified.root.0.is_zero() { Checkpoint { @@ -62,7 +82,7 @@ pub fn get_forkchoice_store( safe_target: block_root, latest_justified, latest_finalized, - blocks: [(block_root, anchor_block)].into(), + blocks: [(block_root, block)].into(), states: [(block_root, anchor_state)].into(), latest_known_attestations: HashMap::new(), latest_new_attestations: HashMap::new(), @@ -75,37 +95,37 @@ pub fn get_forkchoice_store( pub fn get_fork_choice_head( store: &Store, mut root: Root, - latest_attestations: &HashMap, + latest_attestations: &HashMap, min_votes: usize, ) -> Root { if root.0.is_zero() { root = store .blocks .iter() - .min_by_key(|(_, block)| block.message.block.slot) + .min_by_key(|(_, block)| block.slot) .map(|(r, _)| *r) .expect("Error: Empty block."); } let mut vote_weights: HashMap = HashMap::new(); - let root_slot = store.blocks[&root].message.block.slot; + let root_slot = store.blocks[&root].slot; // stage 1: accumulate weights by walking up from each attestation's head - for attestation in latest_attestations.values() { - let mut curr = attestation.message.head.root; + for attestation_data in latest_attestations.values() { + let mut curr = attestation_data.head.root; if let Some(block) = store.blocks.get(&curr) { - let mut curr_slot = block.message.block.slot; + let mut curr_slot = block.slot; while curr_slot > root_slot { *vote_weights.entry(curr).or_insert(0) += 1; if let Some(parent_block) = store.blocks.get(&curr) { - curr = parent_block.message.block.parent_root; + curr = parent_block.parent_root; if curr.0.is_zero() { break; } if let Some(next_block) = store.blocks.get(&curr) { - curr_slot = next_block.message.block.slot; + curr_slot = next_block.slot; } else { break; } @@ -116,13 +136,13 @@ pub fn get_fork_choice_head( } } - // stage 2 + // stage 2: build adjacency tree (parent -> children) let mut child_map: HashMap> = HashMap::new(); for (block_hash, block) in &store.blocks { - if !block.message.block.parent_root.0.is_zero() { + if !block.parent_root.0.is_zero() { if vote_weights.get(block_hash).copied().unwrap_or(0) >= min_votes { child_map - .entry(block.message.block.parent_root) + .entry(block.parent_root) .or_default() .push(*block_hash); } @@ -138,7 +158,6 @@ pub fn get_fork_choice_head( }; // Choose best child: most attestations, then lexicographically highest hash - // This matches leanSpec: max(children, key=lambda x: (weights[x], x)) curr = *children .iter() .max_by(|&&a, &&b| { @@ -190,8 +209,8 @@ pub fn accept_new_attestations(store: &mut Store) { pub fn tick_interval(store: &mut Store, has_proposal: bool) { store.time += 1; - // Calculate current interval within slot: time % SECONDS_PER_SLOT % INTERVALS_PER_SLOT - let curr_interval = (store.time % SECONDS_PER_SLOT) % INTERVALS_PER_SLOT; + // Calculate current interval within slot: time % INTERVALS_PER_SLOT + let curr_interval = store.time % INTERVALS_PER_SLOT; match curr_interval { 0 if has_proposal => accept_new_attestations(store), @@ -201,45 +220,31 @@ pub fn tick_interval(store: &mut Store, has_proposal: bool) { } } +/// Algorithm: +/// 1. Start at Head: Begin with the current head block +/// 2. Walk Toward Safe: Move backward (up to JUSTIFICATION_LOOKBACK_SLOTS steps) +/// if safe target is newer +/// 3. Ensure Justifiable: Continue walking back until slot is justifiable +/// 4. Return Checkpoint: Create checkpoint from selected block pub fn get_vote_target(store: &Store) -> Checkpoint { let mut target = store.head; - let safe_slot = store.blocks[&store.safe_target].message.block.slot; - let source_slot = store.latest_justified.slot; + let safe_slot = store.blocks[&store.safe_target].slot; - // Walk back toward safe target (up to 3 steps per leanSpec JUSTIFICATION_LOOKBACK_SLOTS) + // Walk back toward safe target for _ in 0..3 { - if store.blocks[&target].message.block.slot > safe_slot { - let parent = store.blocks[&target].message.block.parent_root; - // Don't walk back if it would make target <= source (invalid attestation) - if let Some(parent_block) = store.blocks.get(&parent) { - if parent_block.message.block.slot <= source_slot { - break; - } - } - target = parent; + if store.blocks[&target].slot > safe_slot { + target = store.blocks[&target].parent_root; } else { break; } } let final_slot = store.latest_finalized.slot; - while !store.blocks[&target] - .message - .block - .slot - .is_justifiable_after(final_slot) - { - let parent = store.blocks[&target].message.block.parent_root; - // Don't walk back if it would make target <= source (invalid attestation) - if let Some(parent_block) = store.blocks.get(&parent) { - if parent_block.message.block.slot <= source_slot { - break; - } - } - target = parent; + while !store.blocks[&target].slot.is_justifiable_after(final_slot) { + target = store.blocks[&target].parent_root; } - let block_target = &store.blocks[&target].message.block; + let block_target = &store.blocks[&target]; Checkpoint { root: target, slot: block_target.slot, @@ -255,7 +260,7 @@ pub fn get_proposal_head(store: &mut Store, slot: Slot) -> Root { store.head } -/// Produce a block and aggregated signature proofs for the target slot. +/// Produce a block and aggregated signature proofs for the target slot per devnet-2. /// /// The proposer returns the block and `MultisigAggregatedSignature` proofs aligned /// with `block.body.attestations` so it can craft `SignedBlockWithAttestation`. @@ -306,12 +311,13 @@ pub fn produce_block_with_signatures( } // Convert AttestationData to Attestation objects for build_block + // Per devnet-2, store now holds AttestationData directly let available_attestations: Vec = store .latest_known_attestations .iter() - .map(|(validator_idx, signed_att)| Attestation { + .map(|(validator_idx, attestation_data)| Attestation { validator_id: containers::Uint64(validator_idx.0), - data: signed_att.message.clone(), + data: attestation_data.clone(), }) .collect(); @@ -335,7 +341,8 @@ pub fn produce_block_with_signatures( // Compute block root let block_root = Bytes32(final_block.hash_tree_root()); - // Store block and state + // Store block and state (per devnet-2, we store the plain Block) + store.blocks.insert(block_root, final_block.clone()); store.states.insert(block_root, final_post_state); Ok((block_root, final_block, signatures)) diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index 33ef9ee..689d20b 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -434,7 +434,8 @@ fn verify_checks( }; if let Some(expected_slot) = checks.head_slot { - let actual_slot = store.blocks[&store.head].message.block.slot.0; + // Per devnet-2, store.blocks now contains Block (not SignedBlockWithAttestation) + let actual_slot = store.blocks[&store.head].slot.0; if actual_slot != expected_slot { return Err(format!( "Step {}: Head slot mismatch - expected {}, got {}", @@ -448,15 +449,12 @@ fn verify_checks( .get(label) .ok_or_else(|| format!("Step {}: Block label '{}' not found", step_idx, label))?; if &store.head != expected_root { - let actual_slot = store - .blocks - .get(&store.head) - .map(|b| b.message.block.slot.0) - .unwrap_or(0); + // Per devnet-2, store.blocks now contains Block (not SignedBlockWithAttestation) + let actual_slot = store.blocks.get(&store.head).map(|b| b.slot.0).unwrap_or(0); let expected_slot = store .blocks .get(expected_root) - .map(|b| b.message.block.slot.0) + .map(|b| b.slot.0) .unwrap_or(0); return Err(format!( "Step {}: Head root mismatch for label '{}' - expected slot {}, got slot {} (known_attestations: {}, new_attestations: {})", @@ -479,11 +477,12 @@ fn verify_checks( )); } if let Some(target_slot) = check.target_slot { - let attestation = &store.latest_new_attestations[&validator]; - if attestation.message.target.slot.0 != target_slot { + // Per devnet-2, store now holds AttestationData directly (not SignedAttestation) + let attestation_data = &store.latest_new_attestations[&validator]; + if attestation_data.target.slot.0 != target_slot { return Err(format!( "Step {}: Validator {} new attestation target slot mismatch - expected {}, got {}", - step_idx, check.validator, target_slot, attestation.message.target.slot.0 + step_idx, check.validator, target_slot, attestation_data.target.slot.0 )); } } diff --git a/lean_client/fork_choice/tests/unit_tests/fork_choice.rs b/lean_client/fork_choice/tests/unit_tests/fork_choice.rs index d2b3833..684a799 100644 --- a/lean_client/fork_choice/tests/unit_tests/fork_choice.rs +++ b/lean_client/fork_choice/tests/unit_tests/fork_choice.rs @@ -23,7 +23,7 @@ fn test_get_proposal_head_advances_time() { #[test] fn test_get_vote_target_chain() { use containers::{ - block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, + block::{Block, BlockBody}, Bytes32, ValidatorIndex, }; use ssz::SszHash; @@ -32,6 +32,7 @@ fn test_get_vote_target_chain() { let mut parent_root = store.head; // Create a chain of 10 blocks + // Per leanSpec, store.blocks now contains Block (not SignedBlockWithAttestation) for i in 1..=10 { let block = Block { slot: Slot(i), @@ -43,15 +44,8 @@ fn test_get_vote_target_chain() { let block_root = Bytes32(block.hash_tree_root()); - let signed_block = SignedBlockWithAttestation { - message: BlockWithAttestation { - block: block.clone(), - proposer_attestation: Default::default(), - }, - signature: Default::default(), - }; - - store.blocks.insert(block_root, signed_block); + // Insert Block directly per leanSpec + store.blocks.insert(block_root, block); parent_root = block_root; } diff --git a/lean_client/fork_choice/tests/unit_tests/votes.rs b/lean_client/fork_choice/tests/unit_tests/votes.rs index a3c9b8a..2b20fe2 100644 --- a/lean_client/fork_choice/tests/unit_tests/votes.rs +++ b/lean_client/fork_choice/tests/unit_tests/votes.rs @@ -1,12 +1,12 @@ //! Vote/attestation unit tests for devnet2 //! //! Tests for vote processing and fork choice weight calculations -//! using the devnet2 SignedAttestation structure. +//! using the devnet2 AttestationData structure per leanSpec. use super::common::create_test_store; use containers::{ - attestation::{AttestationData, SignedAttestation}, - block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, + attestation::AttestationData, + block::{Block, BlockBody}, checkpoint::Checkpoint, Bytes32, Slot, ValidatorIndex, }; @@ -14,9 +14,8 @@ use fork_choice::store::get_fork_choice_head; use ssz::SszHash; use std::collections::HashMap; -/// Helper to create a SignedAttestation for devnet2 -fn create_signed_attestation( - validator_id: u64, +/// Helper to create an AttestationData for devnet2 (per leanSpec) +fn create_attestation_data( slot: u64, head_root: Bytes32, head_slot: u64, @@ -24,25 +23,21 @@ fn create_signed_attestation( target_slot: u64, source_root: Bytes32, source_slot: u64, -) -> SignedAttestation { - SignedAttestation { - validator_id, - message: AttestationData { - slot: Slot(slot), - head: Checkpoint { - root: head_root, - slot: Slot(head_slot), - }, - target: Checkpoint { - root: target_root, - slot: Slot(target_slot), - }, - source: Checkpoint { - root: source_root, - slot: Slot(source_slot), - }, +) -> AttestationData { + AttestationData { + slot: Slot(slot), + head: Checkpoint { + root: head_root, + slot: Slot(head_slot), + }, + target: Checkpoint { + root: target_root, + slot: Slot(target_slot), + }, + source: Checkpoint { + root: source_root, + slot: Slot(source_slot), }, - signature: Default::default(), } } @@ -52,8 +47,7 @@ fn test_single_vote_updates_head() { let genesis_root = store.head; // Create attestation pointing to genesis - let attestation = create_signed_attestation( - 0, // validator_id + let attestation = create_attestation_data( 1, // slot genesis_root, // head_root 0, // head_slot @@ -81,7 +75,7 @@ fn test_multiple_votes_same_block() { let mut attestations = HashMap::new(); for i in 0..5 { let attestation = - create_signed_attestation(i, 1, genesis_root, 0, genesis_root, 0, genesis_root, 0); + create_attestation_data(1, genesis_root, 0, genesis_root, 0, genesis_root, 0); attestations.insert(ValidatorIndex(i), attestation); } @@ -110,40 +104,22 @@ fn test_competing_votes_different_blocks() { block_b.proposer_index = ValidatorIndex(1); // Different proposer to get different root let block_b_root = Bytes32(block_b.hash_tree_root()); - store.blocks.insert( - block_a_root, - SignedBlockWithAttestation { - message: BlockWithAttestation { - block: block_a, - proposer_attestation: Default::default(), - }, - signature: Default::default(), - }, - ); - - store.blocks.insert( - block_b_root, - SignedBlockWithAttestation { - message: BlockWithAttestation { - block: block_b, - proposer_attestation: Default::default(), - }, - signature: Default::default(), - }, - ); + // Per leanSpec, store.blocks contains Block directly + store.blocks.insert(block_a_root, block_a); + store.blocks.insert(block_b_root, block_b); // 3 votes for block_a, 2 votes for block_b let mut attestations = HashMap::new(); for i in 0..3 { attestations.insert( ValidatorIndex(i), - create_signed_attestation(i, 1, block_a_root, 1, genesis_root, 0, genesis_root, 0), + create_attestation_data(1, block_a_root, 1, genesis_root, 0, genesis_root, 0), ); } for i in 3..5 { attestations.insert( ValidatorIndex(i), - create_signed_attestation(i, 1, block_b_root, 1, genesis_root, 0, genesis_root, 0), + create_attestation_data(1, block_b_root, 1, genesis_root, 0, genesis_root, 0), ); } @@ -177,32 +153,15 @@ fn test_vote_weight_accumulation() { }; let block2_root = Bytes32(block2.hash_tree_root()); - store.blocks.insert( - block1_root, - SignedBlockWithAttestation { - message: BlockWithAttestation { - block: block1, - proposer_attestation: Default::default(), - }, - signature: Default::default(), - }, - ); - store.blocks.insert( - block2_root, - SignedBlockWithAttestation { - message: BlockWithAttestation { - block: block2, - proposer_attestation: Default::default(), - }, - signature: Default::default(), - }, - ); + // Per leanSpec, store.blocks contains Block directly + store.blocks.insert(block1_root, block1); + store.blocks.insert(block2_root, block2); // Vote for block2 - should accumulate to block1 as well let mut attestations = HashMap::new(); attestations.insert( ValidatorIndex(0), - create_signed_attestation(0, 2, block2_root, 2, genesis_root, 0, genesis_root, 0), + create_attestation_data(2, block2_root, 2, genesis_root, 0, genesis_root, 0), ); let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); @@ -222,13 +181,13 @@ fn test_duplicate_vote_uses_latest() { // Insert a vote attestations.insert( ValidatorIndex(0), - create_signed_attestation(0, 1, genesis_root, 0, genesis_root, 0, genesis_root, 0), + create_attestation_data(1, genesis_root, 0, genesis_root, 0, genesis_root, 0), ); // "Update" with same validator - only latest is kept attestations.insert( ValidatorIndex(0), - create_signed_attestation(0, 2, genesis_root, 0, genesis_root, 0, genesis_root, 0), + create_attestation_data(2, genesis_root, 0, genesis_root, 0, genesis_root, 0), ); // Should only have 1 attestation @@ -248,7 +207,7 @@ fn test_vote_for_unknown_block_ignored() { let mut attestations = HashMap::new(); attestations.insert( ValidatorIndex(0), - create_signed_attestation(0, 1, unknown_root, 1, genesis_root, 0, genesis_root, 0), + create_attestation_data(1, unknown_root, 1, genesis_root, 0, genesis_root, 0), ); let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); diff --git a/lean_client/networking/src/enr_ext.rs b/lean_client/networking/src/enr_ext.rs index d7511e4..c515b92 100644 --- a/lean_client/networking/src/enr_ext.rs +++ b/lean_client/networking/src/enr_ext.rs @@ -217,13 +217,16 @@ impl EnrExt for Enr { } /// Returns a list of multiaddrs if the ENR has an `ip` and a `quic` key **or** an `ip6` and a `quic6`. + /// This also appends the `PeerId` into each multiaddr with the `P2p` protocol. fn multiaddr_quic(&self) -> Vec { + let peer_id = self.peer_id(); let mut multiaddrs: Vec = Vec::new(); if let Some(quic_port) = self.quic4() { if let Some(ip) = self.ip4() { let mut multiaddr: Multiaddr = ip.into(); multiaddr.push(Protocol::Udp(quic_port)); multiaddr.push(Protocol::QuicV1); + multiaddr.push(Protocol::P2p(peer_id)); multiaddrs.push(multiaddr); } } @@ -233,6 +236,7 @@ impl EnrExt for Enr { let mut multiaddr: Multiaddr = ip6.into(); multiaddr.push(Protocol::Udp(quic6_port)); multiaddr.push(Protocol::QuicV1); + multiaddr.push(Protocol::P2p(peer_id)); multiaddrs.push(multiaddr); } } diff --git a/lean_client/networking/src/gossipsub/config.rs b/lean_client/networking/src/gossipsub/config.rs index 67061bc..68bb069 100644 --- a/lean_client/networking/src/gossipsub/config.rs +++ b/lean_client/networking/src/gossipsub/config.rs @@ -14,7 +14,7 @@ pub struct GossipsubConfig { impl GossipsubConfig { pub fn new() -> Self { let justification_lookback_slots: u64 = 3; - let seconds_per_slot: u64 = 12; + let seconds_per_slot: u64 = 4; let seen_ttl_secs = seconds_per_slot * justification_lookback_slots * 2; @@ -61,7 +61,7 @@ pub fn compute_message_id(message: &Message) -> MessageId { let topic_len = topic_bytes.len() as u64; let mut digest_input = Vec::new(); - // Domain: 4 bytes + // Domain: 1 byte digest_input.extend_from_slice(MESSAGE_DOMAIN_VALID_SNAPPY); // Topic length: 8 bytes (uint64 little-endian) digest_input.extend_from_slice(&topic_len.to_le_bytes()); diff --git a/lean_client/networking/src/req_resp.rs b/lean_client/networking/src/req_resp.rs index bd6c414..592c746 100644 --- a/lean_client/networking/src/req_resp.rs +++ b/lean_client/networking/src/req_resp.rs @@ -14,7 +14,7 @@ use snap::write::FrameEncoder; pub const MAX_REQUEST_BLOCKS: usize = 1024; pub const STATUS_PROTOCOL_V1: &str = "/leanconsensus/req/status/1/ssz_snappy"; -pub const BLOCKS_BY_ROOT_PROTOCOL_V1: &str = "/leanconsensus/req/lean_blocks_by_root/1/ssz_snappy"; +pub const BLOCKS_BY_ROOT_PROTOCOL_V1: &str = "/leanconsensus/req/blocks_by_root/1/ssz_snappy"; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct LeanProtocol(pub String); diff --git a/lean_client/networking/src/types.rs b/lean_client/networking/src/types.rs index 124ca5a..4e7bde8 100644 --- a/lean_client/networking/src/types.rs +++ b/lean_client/networking/src/types.rs @@ -3,23 +3,75 @@ use std::{collections::HashMap, fmt::Display}; use anyhow::{Result, anyhow}; use async_trait::async_trait; use containers::{Bytes32, SignedAttestation, SignedBlockWithAttestation}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use tokio::sync::mpsc; use crate::serde_utils::quoted_u64; -pub const MESSAGE_DOMAIN_VALID_SNAPPY: &[u8; 4] = &[0x01, 0x00, 0x00, 0x00]; -pub const MESSAGE_DOMAIN_INVALID_SNAPPY: &[u8; 4] = &[0x00, 0x00, 0x00, 0x00]; +/// 1-byte domain for gossip message-id isolation of valid snappy messages. +/// Per leanSpec, prepended to the message hash when decompression succeeds. +pub const MESSAGE_DOMAIN_VALID_SNAPPY: &[u8; 1] = &[0x01]; +/// 1-byte domain for gossip message-id isolation of invalid snappy messages. +/// Per leanSpec, prepended to the message hash when decompression fails. +pub const MESSAGE_DOMAIN_INVALID_SNAPPY: &[u8; 1] = &[0x00]; + +/// Peer connection state machine per leanSpec. +/// +/// Tracks the lifecycle of a connection to a peer: +/// DISCONNECTED -> CONNECTING -> CONNECTED -> DISCONNECTING -> DISCONNECTED +/// +/// These states map directly to libp2p connection events. #[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq, Hash)] #[serde(rename_all = "lowercase")] pub enum ConnectionState { - Connected, - Connecting, + /// No active connection to this peer. Disconnected, + /// TCP/QUIC connection in progress. + Connecting, + /// Transport established, can exchange protocol messages. + Connected, + /// Graceful shutdown in progress (Goodbye sent/received). Disconnecting, } +/// Reason codes for the Goodbye request/response message per leanSpec. +/// +/// Sent when gracefully disconnecting from a peer to indicate why +/// the connection is being closed. +/// +/// Official codes (from spec): +/// - 1: Client shutdown +/// - 2: Irrelevant network +/// - 3: Fault/error +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[repr(u64)] +pub enum GoodbyeReason { + /// Node is shutting down normally. + ClientShutdown = 1, + /// Peer is on a different fork or network. + IrrelevantNetwork = 2, + /// Generic error detected in peer communication. + FaultOrError = 3, +} + +impl GoodbyeReason { + /// Convert from u64 code to GoodbyeReason. + pub fn from_code(code: u64) -> Option { + match code { + 1 => Some(GoodbyeReason::ClientShutdown), + 2 => Some(GoodbyeReason::IrrelevantNetwork), + 3 => Some(GoodbyeReason::FaultOrError), + _ => None, + } + } + + /// Get the u64 code for this reason. + pub fn code(&self) -> u64 { + *self as u64 + } +} + #[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum Direction { diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index 61a4d14..c32a4c5 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -44,11 +44,8 @@ fn load_node_key(path: &str) -> Result> { fn print_chain_status(store: &Store, connected_peers: u64) { let current_slot = store.time / INTERVALS_PER_SLOT; - let head_slot = store - .blocks - .get(&store.head) - .map(|b| b.message.block.slot.0) - .unwrap_or(0); + // Per leanSpec, store.blocks now contains Block (not SignedBlockWithAttestation) + let head_slot = store.blocks.get(&store.head).map(|b| b.slot.0).unwrap_or(0); let behind = if current_slot > head_slot { current_slot - head_slot @@ -56,10 +53,11 @@ fn print_chain_status(store: &Store, connected_peers: u64) { 0 }; + // Per leanSpec, store.blocks now contains Block (not SignedBlockWithAttestation) let (head_root, parent_root, state_root) = if let Some(block) = store.blocks.get(&store.head) { let head_root = store.head; - let parent_root = block.message.block.parent_root; - let state_root = block.message.block.state_root; + let parent_root = block.parent_root; + let state_root = block.state_root; (head_root, parent_root, state_root) } else { ( diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index cde7b17..f64080e 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -135,10 +135,11 @@ impl ValidatorService { let vote_target = get_vote_target(store); - // Validate that target slot is strictly greater than source slot - if vote_target.slot <= store.latest_justified.slot { + // Validate that target slot is greater than or equal to source slot + // At genesis, both target and source are slot 0, which is valid + if vote_target.slot < store.latest_justified.slot { return Err(format!( - "Invalid attestation: target slot {} must be greater than source slot {}", + "Invalid attestation: target slot {} must be >= source slot {}", vote_target.slot.0, store.latest_justified.slot.0 )); } @@ -149,7 +150,7 @@ impl ValidatorService { .ok_or("Head block not found")?; let head_checkpoint = Checkpoint { root: store.head, - slot: head_block.message.block.slot, + slot: head_block.slot, }; let proposer_attestation = Attestation { @@ -169,12 +170,10 @@ impl ValidatorService { // 1. Have source matching the parent state's justified checkpoint // 2. Have target slot > source slot (valid attestations) // 3. Target block must be known - // Also collect the corresponding signatures - let valid_signed_attestations: Vec<&SignedAttestation> = store + let valid_attestations: Vec = store .latest_new_attestations .values() - .filter(|att| { - let data = &att.message; + .filter(|data| { // Source must match the parent state's justified checkpoint (not store's!) let source_matches = data.source == parent_state.latest_justified; // Target must be strictly after source @@ -184,11 +183,7 @@ impl ValidatorService { source_matches && target_after_source && target_known }) - .collect(); - - let valid_attestations: Vec = valid_signed_attestations - .iter() - .map(|att| att.message.clone()) + .cloned() .collect(); info!( @@ -226,7 +221,7 @@ impl ValidatorService { proposer = block.proposer_index.0, parent_root = %format!("0x{:x}", block.parent_root.0), state_root = %format!("0x{:x}", block.state_root.0), - attestation_sigs = valid_signed_attestations.len(), + attestation_sigs = valid_attestations.len(), "Block built successfully" ); @@ -282,22 +277,20 @@ impl ValidatorService { pub fn create_attestations(&self, store: &Store, slot: Slot) -> Vec { let vote_target = get_vote_target(store); - // Skip attestation creation if target slot is not strictly greater than source slot - // This prevents creating invalid attestations when the node's view is behind - if vote_target.slot <= store.latest_justified.slot { + // Skip attestation creation if target slot is less than source slot + // At genesis, both target and source are slot 0, which is valid + if vote_target.slot < store.latest_justified.slot { warn!( target_slot = vote_target.slot.0, source_slot = store.latest_justified.slot.0, - "Skipping attestation: target slot must be greater than source slot" + "Skipping attestation: target slot must be >= source slot" ); return vec![]; } - let get_head_block_info = match store.blocks.get(&store.head) { + let head_block = match store.blocks.get(&store.head) { Some(b) => b, None => { - // Pasileiskit, su DEBUG. Kitaip galima pakeist i tiesiog - // println!("WARNING: Attestation skipped. (Reason: HEAD BLOCK NOT FOUND)\n"); warn!("WARNING: Attestation skipped. (Reason: HEAD BLOCK NOT FOUND)"); return vec![]; } @@ -305,7 +298,7 @@ impl ValidatorService { let head_checkpoint = Checkpoint { root: store.head, - slot: get_head_block_info.message.block.slot, + slot: head_block.slot, }; self.config From 333be405bdda836b84238dea894bb6ad3754b0d4 Mon Sep 17 00:00:00 2001 From: Nojus Sandovas Date: Mon, 26 Jan 2026 00:52:06 +0200 Subject: [PATCH 19/48] Fixed justified and finalized slots not updating --- lean_client/Cargo.lock | 1 + lean_client/containers/Cargo.toml | 1 + lean_client/containers/src/state.rs | 100 ++++++++++++++++- lean_client/networking/src/network/service.rs | 12 +- lean_client/validator/src/lib.rs | 106 +++++++++++++----- 5 files changed, 188 insertions(+), 32 deletions(-) diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index 4aaaef3..bce89e2 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -925,6 +925,7 @@ dependencies = [ "ssz", "ssz_derive", "test-generator", + "tracing", "typenum", ] diff --git a/lean_client/containers/Cargo.toml b/lean_client/containers/Cargo.toml index 1b8e8e5..282fc6b 100644 --- a/lean_client/containers/Cargo.toml +++ b/lean_client/containers/Cargo.toml @@ -16,6 +16,7 @@ env-config = { path = "../env-config", default-features = false } ssz = { workspace = true } serde = { workspace = true } ssz_derive = { workspace = true } +tracing = "0.1" typenum = "1" serde_json = "1.0" serde_yaml = "0.9" diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index 5bb8a9e..cf8db72 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -399,6 +399,12 @@ impl State { let initial_finalized_slot = self.latest_finalized.slot; let justified_slots = self.justified_slots.clone(); + tracing::info!( + current_justified_slot = latest_justified.slot.0, + current_finalized_slot = latest_finalized.slot.0, + "Processing attestations in block" + ); + let mut justified_slots_working = Vec::new(); for i in 0..justified_slots.len() { justified_slots_working.push(justified_slots.get(i).map(|b| *b).unwrap_or(false)); @@ -455,10 +461,21 @@ impl State { .copied() .unwrap_or(false); + // Special case for slot 0 (genesis): historical_block_hashes[0] is initialized as 0x0 + // in genesis, but validators attest with the actual genesis block hash (set in + // get_forkchoice_store). Allow any source_root when source is slot 0 and + // historical_block_hashes[0] is the zero hash. let source_root_matches = self .historical_block_hashes .get(source_slot_int as u64) - .map(|r| *r == source_root) + .map(|r| { + if source_slot_int == 0 && r.0.is_zero() { + // Genesis slot: accept any root when historical hash is 0x0 + true + } else { + *r == source_root + } + }) .unwrap_or(false); let target_root_matches = self .historical_block_hashes @@ -472,7 +489,31 @@ impl State { && target_slot > source_slot && target_slot.is_justifiable_after(initial_finalized_slot); + // Debug logging for vote validation + tracing::debug!( + source_slot = source_slot.0, + target_slot = target_slot.0, + source_root = %format!("0x{:x}", source_root.0), + target_root = %format!("0x{:x}", target_root.0), + validator_count = validator_ids.len(), + source_is_justified, + target_already_justified, + source_root_matches, + target_root_matches, + is_valid_vote, + "Processing attestation vote" + ); + if !is_valid_vote { + tracing::warn!( + source_slot = source_slot.0, + target_slot = target_slot.0, + source_is_justified, + target_already_justified, + source_root_matches, + target_root_matches, + "Vote rejected" + ); return; } @@ -492,7 +533,25 @@ impl State { if let Some(votes) = justifications.get(&target_root) { let num_validators = self.validators.len_u64() as usize; let count = votes.iter().filter(|&&v| v).count(); + let threshold = (2 * num_validators + 2) / 3; // ceil(2/3) + + tracing::info!( + target_slot = target_slot.0, + target_root = %format!("0x{:x}", target_root.0), + vote_count = count, + num_validators, + threshold, + needs = format!("3*{} >= 2*{} = {} >= {}", count, num_validators, 3*count, 2*num_validators), + will_justify = 3 * count >= 2 * num_validators, + "Vote count for target" + ); + if 3 * count >= 2 * num_validators { + tracing::info!( + target_slot = target_slot.0, + target_root = %format!("0x{:x}", target_root.0), + "JUSTIFICATION THRESHOLD REACHED!" + ); *latest_justified = vote.target.clone(); justified_slots_working.extend(std::iter::repeat_n( @@ -507,6 +566,7 @@ impl State { .all(|s| !Slot(s as u64).is_justifiable_after(initial_finalized_slot)); if is_finalizable { + tracing::info!(source_slot = source_slot.0, "FINALIZATION!"); *latest_finalized = vote.source.clone(); } } @@ -628,11 +688,26 @@ impl State { .map_err(|e| format!("Failed to push attestation: {:?}", e))?; } + // IMPORTANT: Recompute post_state using the FINAL attestations. + // The original post_state was computed from candidate_block with ALL attestations, + // but final_attestations_list may have fewer attestations (only those with signatures). + // We must use the same attestations for state computation and the block body. + let final_candidate_block = Block { + slot, + proposer_index, + parent_root, + state_root: Bytes32(ssz::H256::zero()), + body: BlockBody { + attestations: final_attestations_list.clone(), + }, + }; + let final_post_state = pre_state.process_block(&final_candidate_block)?; + let final_block = Block { slot, proposer_index, parent_root, - state_root: hash_tree_root(&post_state), + state_root: hash_tree_root(&final_post_state), body: BlockBody { attestations: final_attestations_list, }, @@ -640,7 +715,7 @@ impl State { return Ok(( final_block, - post_state, + final_post_state, aggregated_attestations, aggregated_proofs, )); @@ -708,11 +783,26 @@ impl State { .map_err(|e| format!("Failed to push attestation: {:?}", e))?; } + // IMPORTANT: Recompute post_state using the FINAL attestations. + // The original post_state was computed from candidate_block with ALL attestations, + // but final_attestations_list may have fewer attestations (only those with signatures). + // We must use the same attestations for state computation and the block body. + let final_candidate_block = Block { + slot, + proposer_index, + parent_root, + state_root: Bytes32(ssz::H256::zero()), + body: BlockBody { + attestations: final_attestations_list.clone(), + }, + }; + let final_post_state = pre_state.process_block(&final_candidate_block)?; + let final_block = Block { slot, proposer_index, parent_root, - state_root: hash_tree_root(&post_state), + state_root: hash_tree_root(&final_post_state), body: BlockBody { attestations: final_attestations_list, }, @@ -720,7 +810,7 @@ impl State { return Ok(( final_block, - post_state, + final_post_state, aggregated_attestations, aggregated_proofs, )); diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index 1c97353..47eaa9f 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -584,7 +584,11 @@ where match signed_block_with_attestation.to_ssz() { Ok(bytes) => { if let Err(err) = self.publish_to_topic(GossipsubKind::Block, bytes) { - warn!(slot = slot, ?err, "Publish block with attestation failed"); + // Duplicate errors are expected - we receive our own blocks back from peers + let err_str = format!("{:?}", err); + if !err_str.contains("Duplicate") { + warn!(slot = slot, ?err, "Publish block with attestation failed"); + } } else { info!(slot = slot, "Broadcasted block with attestation"); } @@ -600,7 +604,11 @@ where match signed_attestation.to_ssz() { Ok(bytes) => { if let Err(err) = self.publish_to_topic(GossipsubKind::Attestation, bytes) { - warn!(slot = slot, ?err, "Publish attestation failed"); + // Duplicate errors are expected - we receive our own attestations back from peers + let err_str = format!("{:?}", err); + if !err_str.contains("Duplicate") { + warn!(slot = slot, ?err, "Publish attestation failed"); + } } else { info!(slot = slot, "Broadcasted attestation"); } diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index f64080e..f84b70a 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -163,17 +163,20 @@ impl ValidatorService { }, }; - // Collect valid attestations from the NEW attestations pool (gossip attestations - // that haven't been included in any block yet). - // Do NOT use latest_known_attestations - those have already been included in blocks! + // Collect valid attestations from the KNOWN attestations pool. + // Note: get_proposal_head() calls accept_new_attestations() which moves attestations + // from latest_new_attestations to latest_known_attestations. So we must read from + // latest_known_attestations here, not latest_new_attestations. // Filter to only include attestations that: // 1. Have source matching the parent state's justified checkpoint // 2. Have target slot > source slot (valid attestations) // 3. Target block must be known - let valid_attestations: Vec = store - .latest_new_attestations - .values() - .filter(|data| { + // 4. Target is not already justified in parent state + // 5. Source is justified in parent state + let valid_attestations: Vec = store + .latest_known_attestations + .iter() + .filter(|(_, data)| { // Source must match the parent state's justified checkpoint (not store's!) let source_matches = data.source == parent_state.latest_justified; // Target must be strictly after source @@ -181,36 +184,89 @@ impl ValidatorService { // Target block must be known let target_known = store.blocks.contains_key(&data.target.root); - source_matches && target_after_source && target_known + // Check if target is NOT already justified (matching process_single_attestation) + let target_slot_idx = data.target.slot.0 as usize; + let target_already_justified = parent_state + .justified_slots + .get(target_slot_idx) + .map(|b| *b) + .unwrap_or(false); + + // Check if source is justified + let source_slot_idx = data.source.slot.0 as usize; + let source_is_justified = parent_state + .justified_slots + .get(source_slot_idx) + .map(|b| *b) + .unwrap_or(false); + + source_matches + && target_after_source + && target_known + && source_is_justified + && !target_already_justified + }) + .map(|(validator_idx, data)| Attestation { + validator_id: Uint64(validator_idx.0), + data: data.clone(), + }) + .collect(); + + // De-duplicate by target slot: only include ONE aggregated attestation per target slot. + // This prevents the case where the first attestation justifies a slot and the second + // gets rejected (causing state root mismatch). + // Group by target slot, keeping attestations with the most common AttestationData. + use std::collections::HashMap; + + // First group by target slot + let mut target_slot_groups: HashMap> = HashMap::new(); + for att in valid_attestations { + let target_slot = att.data.target.slot.0; + target_slot_groups.entry(target_slot).or_default().push(att); + } + + // For each target slot, group by data root and pick the one with most votes + let valid_attestations: Vec = target_slot_groups + .into_iter() + .flat_map(|(_, slot_atts)| { + // Group by data root (Bytes32 implements Hash) + let mut data_groups: HashMap> = + HashMap::new(); + for att in slot_atts { + let data_root = att.data.data_root_bytes(); + data_groups.entry(data_root).or_default().push(att); + } + // Find the data with the most attestations + data_groups + .into_iter() + .max_by_key(|(_, atts)| atts.len()) + .map(|(_, atts)| atts) + .unwrap_or_default() }) - .cloned() .collect(); + let num_attestations = valid_attestations.len(); + info!( slot = slot.0, - valid_attestations = valid_attestations.len(), - total_new = store.latest_new_attestations.len(), - "Collected new attestations for block" + valid_attestations = num_attestations, + total_known = store.latest_known_attestations.len(), + "Collected attestations for block" ); - // Build block with collected attestations (empty body - attestations go to state) + // Build block with collected attestations + // Pass gossip_signatures and aggregated_payloads from the store so that + // compute_aggregated_signatures can find signatures for the attestations let (block, _post_state, _collected_atts, sigs) = { - let valid_attestations: Vec = valid_attestations - .iter() - .map(|data| Attestation { - validator_id: Uint64(0), // Placeholder, real validator IDs should be used - data: data.clone(), - }) - .collect(); parent_state.build_block( slot, proposer_index, parent_root, Some(valid_attestations), - None, - None, - None, - None, + None, // available_attestations + None, // known_block_roots + Some(&store.gossip_signatures), // gossip_signatures + Some(&store.aggregated_payloads), // aggregated_payloads )? }; @@ -221,7 +277,7 @@ impl ValidatorService { proposer = block.proposer_index.0, parent_root = %format!("0x{:x}", block.parent_root.0), state_root = %format!("0x{:x}", block.state_root.0), - attestation_sigs = valid_attestations.len(), + attestation_sigs = num_attestations, "Block built successfully" ); From 66e3b9612232089fcb873d76c2a22e16eafd86ba Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:18:02 +0200 Subject: [PATCH 20/48] Update test vectors --- lean_client/Makefile | 2 +- ...ation_accumulation_full_validator_set.json | 54 +-- ...ttestation_superseding_same_validator.json | 34 +- ...stations_move_to_known_between_blocks.json | 34 +- ...chain_attestation_superseding_pattern.json | 94 +++--- ...ser_attestation_appears_in_latest_new.json | 24 +- ...lot_gaps_with_attestation_superseding.json | 54 +-- ...ion_target_advances_with_attestations.json | 64 ++-- ...testation_target_at_genesis_initially.json | 34 +- ...station_target_justifiable_constraint.json | 314 +++++++++--------- ...ttestation_target_with_extended_chain.json | 94 +++--- ...est_attestation_target_with_slot_gaps.json | 44 +-- ...test_head_advances_through_deep_chain.json | 216 ++++++------ .../test_head_switches_to_heavier_fork.json | 62 ++-- .../test_head_with_deep_fork_split.json | 110 +++--- .../test_head_with_gaps_in_slots.json | 64 ++-- .../test_head_with_large_gaps.json | 54 +-- .../test_head_with_two_competing_forks.json | 50 +-- ...test_back_and_forth_reorg_oscillation.json | 114 +++---- .../test_reorg_on_newly_justified_slot.json | 104 +++--- ..._heavy_fork_resists_light_competition.json | 138 ++++---- .../test_reorg_with_slot_gaps.json | 98 +++--- .../test_simple_one_block_reorg.json | 62 ++-- .../test_three_block_deep_reorg.json | 114 +++---- .../test_three_way_fork_competition.json | 98 +++--- ..._two_block_reorg_progressive_building.json | 86 ++--- ...ht_forks_use_lexicographic_tiebreaker.json | 72 ++-- .../test_block_at_large_slot_number.json | 16 +- .../test_block_extends_deep_chain.json | 92 ++--- .../test_block_with_invalid_parent_root.json | 12 +- .../test_block_with_invalid_proposer.json | 14 +- .../test_block_with_invalid_state_root.json | 14 +- .../test_block_with_wrong_slot.json | 16 +- .../test_blocks_with_gaps.json | 24 +- .../test_empty_blocks.json | 36 +- .../test_empty_blocks_with_missed_slots.json | 32 +- .../test_linear_chain_multiple_blocks.json | 32 +- ...est_process_first_block_after_genesis.json | 16 +- .../test_genesis_custom_time.json | 12 +- .../test_genesis_custom_validator_set.json | 20 +- .../test_genesis_default_configuration.json | 12 +- .../test_invalid_signature.json | 2 +- ...test_proposer_and_attester_signatures.json | 6 +- .../test_proposer_signature.json | 2 +- 44 files changed, 1323 insertions(+), 1323 deletions(-) diff --git a/lean_client/Makefile b/lean_client/Makefile index 5dee5e1..6153df6 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -53,7 +53,7 @@ generate-test-vectors: git remote add origin https://github.com/leanEthereum/leanSpec.git && \ git fetch --depth 1 origin $(LEAN_SPEC_COMMIT) && \ git switch --detach FETCH_HEAD - cd spec && uv run fill --clean --fork=devnet + cd spec && uv run fill --clean --fork=devnet --scheme prod rm -rf ./test_vectors && mkdir -p ./test_vectors cp -r ./spec/fixtures/consensus/* ./test_vectors/ diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json index 2f9d0f0..22dba6e 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_accumulation_full_validator_set[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -85,8 +85,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -98,15 +98,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -136,8 +136,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -149,15 +149,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -192,8 +192,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -205,15 +205,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -253,8 +253,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -266,15 +266,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -284,7 +284,7 @@ ], "maxSlot": 4, "_info": { - "hash": "0x20a5a607df3a6b554e24236dd46d2e751befee0a2b04716e5c2022251881d54a", + "hash": "0xdbbeaea6e8f9a310bf70b5d73ca417b937d913619861e17b159518e04f574985", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_accumulation_full_validator_set[fork_Devnet]", "description": "All validators contribute attestations across both dictionaries.\n\n Scenario\n --------\n Process blocks at slots 1, 2, 3, 4 (complete validator rotation).\n\n Expected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - After slot 3: new attestations = 1, known attestations = 2\n - After slot 4: new attestations = 1, known attestations = 3 (total: 4 validators)\n\n Why This Matters\n ----------------\n With 4 validators and consecutive blocks, each validator proposes once.\n\n Attestations accumulate across both dictionaries:\n - new: current slot's proposer\n - known: all previous proposers\n\n The total (new + known) equals the number of unique validators who proposed.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json index 20b33c4..318541a 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_superseding_same_validator[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -87,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -100,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -134,8 +134,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xd13d7468177a38a08f5e93fdbca977304166c9a4abaa6ed54b67e456fc27a965", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xe06ec37b56e45f8fc96a4e078e4822dd801f4cd0a26facc8c746e985ce1d3031", "body": { "attestations": { "data": [] @@ -147,15 +147,15 @@ "data": { "slot": 5, "head": { - "root": "0x2d863bd6e2498d9d8e103c2c7b450e6e27cfcc39fb2e18bfda30076b2a582ebf", + "root": "0x4224313451549d13ea60e62d6c5a62070439107df667561baa1d78ce7676a98f", "slot": 5 }, "target": { - "root": "0x2d863bd6e2498d9d8e103c2c7b450e6e27cfcc39fb2e18bfda30076b2a582ebf", + "root": "0x4224313451549d13ea60e62d6c5a62070439107df667561baa1d78ce7676a98f", "slot": 5 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -165,7 +165,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0xf2546ddba6f4e0623514d90f314e58b92cccacc40b45fc88c6b894060534d3e4", + "hash": "0x5fb3cc07e42126611049361cd37c1dddf1a6a1a7b0a53fb76e5acd6217e60478", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_superseding_same_validator[fork_Devnet]", "description": "Newer attestation from same validator supersedes older attestation.\n\n Scenario\n --------\n Process blocks at slots 1 and 5 (same proposer: validator 1).\n\n Expected:\n - After slot 1: validator 1 attests to slot 1\n - After slot 5: validator 1 attests to slot 5 (supersedes slot 1)\n\n Why This Matters\n ----------------\n With round-robin proposer selection, slots 1 and 5 use the same validator.\n\n When that validator proposes again, their newer attestation supersedes the older one.\n Both dictionaries are keyed by validator index, so only the most recent\n attestation per validator is retained.\n\n Key insight: Attestations accumulate across validators but supersede within validators.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json index 942d14b..a4638e6 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestations_move_to_known_between_blocks[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -87,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -100,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -143,8 +143,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -156,15 +156,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -174,7 +174,7 @@ ], "maxSlot": 2, "_info": { - "hash": "0x23dbfac62912e991a25db677805a292bb1aa6328a62c3796b733eb6c9a1d903a", + "hash": "0xab273bd905d90599241dc8c0c4f4f2a215e7a2a9c7841f4614e8e42dbe5f7890", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestations_move_to_known_between_blocks[fork_Devnet]", "description": "Attestations move from latest_new to latest_known between blocks.\n\n Scenario\n --------\n Process blocks at slots 1 and 2 (different proposers: validators 1 and 2).\n\n Expected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - Validator 1's attestation moved to known with correct checkpoints\n - Validator 2's attestation in new with correct checkpoints\n\n Why This Matters\n ----------------\n The interval tick system drives attestation migration between slots.\n\n Before processing the next block, interval ticks move all attestations from\n new \u2192 known and clear the new dictionary. Then the next block's proposer\n attestation enters the now-empty new dictionary.\n\n This creates the attestation pipeline:\n - Enter via new (arrivals)\n - Graduate to known (accepted for fork choice)", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json index 33780b1..83d02a6 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_processing.py::test_extended_chain_attestation_superseding_pattern[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -84,8 +84,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -97,15 +97,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -134,8 +134,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -147,15 +147,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -189,8 +189,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -202,15 +202,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -249,8 +249,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -262,15 +262,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -309,8 +309,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -322,15 +322,15 @@ "data": { "slot": 5, "head": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "target": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "source": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 } } @@ -369,8 +369,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", - "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", + "parentRoot": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", + "stateRoot": "0x3ccb488b064dae8643040852e10acc9276aa087d85bb82387eb29101245d1d35", "body": { "attestations": { "data": [] @@ -382,15 +382,15 @@ "data": { "slot": 6, "head": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "target": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "source": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 } } @@ -429,8 +429,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", - "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", + "parentRoot": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", + "stateRoot": "0x924c49d73043292db24525a72614bc79453028c75a9fb23b27a0e4e557331560", "body": { "attestations": { "data": [] @@ -442,15 +442,15 @@ "data": { "slot": 7, "head": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "target": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "source": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 } } @@ -489,8 +489,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", - "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", + "parentRoot": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", + "stateRoot": "0xcf16ef20643079955b1ee11f6e73ad0694d6c0a2f97d6732e43dc342d6277bbb", "body": { "attestations": { "data": [] @@ -502,15 +502,15 @@ "data": { "slot": 8, "head": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "target": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "source": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 } } @@ -520,7 +520,7 @@ ], "maxSlot": 8, "_info": { - "hash": "0x8aa34f8496e6fa348aa18faa45fe3e3d3b4b476fb2b65309312839ab889abb56", + "hash": "0xcbdd6766ce8841f678f2e13cd980c3163cb78456053753bc6684cfd3c6a70c83", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_extended_chain_attestation_superseding_pattern[fork_Devnet]", "description": "Attestation superseding pattern over two complete validator rotations.\n\n Scenario\n --------\n Process blocks at slots 1-8 (two complete validator rotations).\n\n Phase 1 (slots 1-4): Accumulation\n Validators each propose once, attestations accumulate to 4 total.\n\n Phase 2 (slots 5-8): Steady State\n Validators propose again, newer attestations supersede older ones.\n Total stays at 4, composition changes.\n\n Expected:\n - After slot 4: All 4 validators have attestations (v0 in new, v1-v3 in known)\n - After slot 5: Validator 1 supersedes their slot 1 attestation\n - After slot 8: All validators have their latest attestations from slots 5-8\n\n Why This Matters\n ----------------\n The system reaches steady state: one attestation per validator.\n\n As each validator proposes again, their new attestation supersedes their old one.\n The count remains constant (4), but the composition updates.\n\n This confirms superseding maintains correct state over time with no attestation\n leaks or unbounded growth.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json index b03c7e7..9224e5d 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_processing.py::test_proposer_attestation_appears_in_latest_new[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -87,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -100,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -118,7 +118,7 @@ ], "maxSlot": 1, "_info": { - "hash": "0xa1b9de2d8ff812fc338af7ce83c6f13c839a7e4cae92a23f9ce9f459a9508586", + "hash": "0x0cfd631610f67a5a5e80a51d3407ca9429e0ffeee888cbf761b379482cc9ef76", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_proposer_attestation_appears_in_latest_new[fork_Devnet]", "description": "Proposer attestation appears in latest_new after block processing.\n\n Scenario\n --------\n Process one block at slot 1 (proposer: validator 1).\n\n Expected:\n - validator 1's attestation has correct slot and checkpoint slots\n\n Why This Matters\n ----------------\n New proposer attestations enter the pipeline through `latest_new_attestations`,\n not directly into `latest_known_attestations`.\n\n This baseline test verifies the entry point of the attestation pipeline.\n All new attestations must enter through the \"new\" stage before graduating to \"known\".", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json index 6e9111c..5180a4c 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_processing.py::test_slot_gaps_with_attestation_superseding[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -85,8 +85,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -98,15 +98,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -136,8 +136,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0x7031a4d5385dcfbe2a9f373845bb8ffd86863aed0d0d87976141c9b11edac5bc", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0x9f9e08828769c720e902e34d8e6c190c92f88856cfdc679df5c46cbbf8b27d4e", "body": { "attestations": { "data": [] @@ -149,15 +149,15 @@ "data": { "slot": 3, "head": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 }, "target": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -187,8 +187,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", - "stateRoot": "0x1ee1961b8157e69e77990196fbab902d16c0f51bd8da1848dfa0a6d8e177ad7c", + "parentRoot": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", + "stateRoot": "0x0276d5a3164729b2862239cd9bb33115ba5ab902bcf0c67c7b2130909d1915a1", "body": { "attestations": { "data": [] @@ -200,15 +200,15 @@ "data": { "slot": 5, "head": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 }, "target": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 }, "source": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 } } @@ -238,8 +238,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", - "stateRoot": "0x784f0f32d518e8a5ccdc7b0887d26d64b2d0048faa696c0616d8ff5a37d02d28", + "parentRoot": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", + "stateRoot": "0x80615d070ddce2d537ff4b4ce5d403cbb794b0fa1a84d5d4e07b30ae72f4d2e8", "body": { "attestations": { "data": [] @@ -251,15 +251,15 @@ "data": { "slot": 7, "head": { - "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", + "root": "0x2891408b24b855513d0d64726c152225194cdf79d36263b66ff894efb8debbe6", "slot": 7 }, "target": { - "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", + "root": "0x2891408b24b855513d0d64726c152225194cdf79d36263b66ff894efb8debbe6", "slot": 7 }, "source": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 } } @@ -269,7 +269,7 @@ ], "maxSlot": 7, "_info": { - "hash": "0xd90d9fbb57bfd2cc79cd04304f4cbc2686ed31e682a732dcf2ddd94e69e9d06b", + "hash": "0x9b83a8ab2fb554ae0ec61bde795000e3a8157ac64960caaebda77db0e6c22abb", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_slot_gaps_with_attestation_superseding[fork_Devnet]", "description": "Attestation superseding works correctly with missed slots.\n\n Scenario\n --------\n Process blocks at slots 1, 3, 5, 7 (skipping even slots).\n Proposers: validators 1, 3, 1, 3 (same validators repeat).\n\n Expected:\n - After slot 1: Validator 1 attests\n - After slot 3: Validator 3 attests, validator 1 moved to known\n - After slot 5: Validator 1 attests again (supersedes old), validator 3 in known\n - After slot 7: Validator 3 attests again (supersedes old), validator 1 in known\n\n Why This Matters\n ----------------\n Missed slots are normal when proposers fail to produce blocks.\n\n With non-contiguous slots, round-robin means validators propose multiple times.\n When they do, their newer attestations supersede their older ones.\n\n Total count stays at 2 (unique validators) throughout slots 5-7.\n\n This confirms attestation processing and superseding work correctly with slot gaps\n across both dictionaries.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json index b6b86d5..b0330ae 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_advances_with_attestations[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -78,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -91,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -117,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -130,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -156,8 +156,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -169,15 +169,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -195,8 +195,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -208,15 +208,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -234,8 +234,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -247,15 +247,15 @@ "data": { "slot": 5, "head": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "target": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "source": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 } } @@ -265,7 +265,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0xb3a2ac78c3dca2def6826b97621593399629f80d66c5f6ed6838d6a8399110c5", + "hash": "0xc067f96d03a7a7954424ae0c3f84c5660206cd637bcf43c1063de04e6297f131", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_advances_with_attestations[fork_Devnet]", "description": "Attestation target advances as attestation weight accumulates.\n\n Scenario\n --------\n Build a longer chain (slots 1-5) where attestations cause target advancement.\n\n Expected:\n - Initial blocks: target stays at genesis (slot 0)\n - Later blocks: target advances as attestations accumulate\n - Target remains behind head for safety\n\n Why This Matters\n ----------------\n As validators attest to blocks, the safe target advances, which in turn\n allows the attestation target to move forward.\n\n This demonstrates the dynamic nature of target selection: conservative initially,\n but advancing as consensus strengthens through attestation accumulation.\n\n The target advances only when sufficient attestation weight supports it.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json index 54fb982..b631916 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_at_genesis_initially[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -78,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -91,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -117,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -130,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -148,7 +148,7 @@ ], "maxSlot": 2, "_info": { - "hash": "0x55931a454acbfd50f0d0ef9f92395af85e7c37f0dcbb87251042c5d4c6e64f39", + "hash": "0x7ed6ed6cb6f816bdff8fb63c80379d445bdcf1d2b4812bb98cdfce907eb3e152", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_at_genesis_initially[fork_Devnet]", "description": "Attestation target starts at genesis before safe target updates.\n\n Scenario\n --------\n Process two blocks at slots 1 and 2.\n\n Expected:\n - After slot 1: target = slot 0 (genesis/finalized)\n - After slot 2: target = slot 0 (genesis/finalized)\n - Target root automatically validated against block at slot 0\n\n Why This Matters\n ----------------\n Initially, the safe target is at genesis (slot 0), so the attestation\n target walks back from head to genesis.\n\n This conservative behavior ensures validators don't attest too far ahead\n before there's sufficient attestation weight to advance the safe target.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json index ab3a6cf..7eb85ce 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_justifiable_constraint[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -78,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -91,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -117,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -130,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -156,8 +156,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -169,15 +169,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -195,8 +195,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -208,15 +208,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -234,8 +234,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -247,15 +247,15 @@ "data": { "slot": 5, "head": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "target": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "source": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 } } @@ -273,8 +273,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", - "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", + "parentRoot": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", + "stateRoot": "0x3ccb488b064dae8643040852e10acc9276aa087d85bb82387eb29101245d1d35", "body": { "attestations": { "data": [] @@ -286,15 +286,15 @@ "data": { "slot": 6, "head": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "target": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "source": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 } } @@ -312,8 +312,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", - "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", + "parentRoot": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", + "stateRoot": "0x924c49d73043292db24525a72614bc79453028c75a9fb23b27a0e4e557331560", "body": { "attestations": { "data": [] @@ -325,15 +325,15 @@ "data": { "slot": 7, "head": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "target": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "source": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 } } @@ -351,8 +351,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", - "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", + "parentRoot": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", + "stateRoot": "0xcf16ef20643079955b1ee11f6e73ad0694d6c0a2f97d6732e43dc342d6277bbb", "body": { "attestations": { "data": [] @@ -364,15 +364,15 @@ "data": { "slot": 8, "head": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "target": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "source": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 } } @@ -390,8 +390,8 @@ "block": { "slot": 9, "proposerIndex": 1, - "parentRoot": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", - "stateRoot": "0xc8f2880bc8e3d1ba655c75f34ae5a20555503ce8296cbc3a2a266fbcdb7b078b", + "parentRoot": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", + "stateRoot": "0x4a76b547a30d61443a8e045ef40912658582ba64fb8c6d483c207f8f7a55f063", "body": { "attestations": { "data": [] @@ -403,15 +403,15 @@ "data": { "slot": 9, "head": { - "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "root": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", "slot": 9 }, "target": { - "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "root": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", "slot": 9 }, "source": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 } } @@ -429,8 +429,8 @@ "block": { "slot": 10, "proposerIndex": 2, - "parentRoot": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", - "stateRoot": "0x054571db4eb9706f6f29a97bffaa00765b2a75b77ad5812e8619066e942d3192", + "parentRoot": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", + "stateRoot": "0xd424ce4fe3a44ae13b83ebefe1efaacf805e00dd4ca5839ce2d0788de4936864", "body": { "attestations": { "data": [] @@ -442,15 +442,15 @@ "data": { "slot": 10, "head": { - "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "root": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", "slot": 10 }, "target": { - "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "root": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", "slot": 10 }, "source": { - "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "root": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", "slot": 9 } } @@ -468,8 +468,8 @@ "block": { "slot": 11, "proposerIndex": 3, - "parentRoot": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", - "stateRoot": "0x4a359bc7767356dd13c761cc98bd9f0a62013636c0395e9babd5d37794c9c728", + "parentRoot": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", + "stateRoot": "0x5bd597860d3f6c089f438b5ae3aebcb1858bb0dd74c09ea84c6f68f4dc1f4880", "body": { "attestations": { "data": [] @@ -481,15 +481,15 @@ "data": { "slot": 11, "head": { - "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "root": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", "slot": 11 }, "target": { - "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "root": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", "slot": 11 }, "source": { - "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "root": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", "slot": 10 } } @@ -507,8 +507,8 @@ "block": { "slot": 12, "proposerIndex": 0, - "parentRoot": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", - "stateRoot": "0x584b4ae5faaeb8a0cc6b190b4f570d773fc3f40426c0eb5a1f1730b199633b0a", + "parentRoot": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", + "stateRoot": "0x2cd6d0d82adf90b137c6f4ba447cd7be99e22ee890568024ba9b51a41cef58e2", "body": { "attestations": { "data": [] @@ -520,15 +520,15 @@ "data": { "slot": 12, "head": { - "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "root": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", "slot": 12 }, "target": { - "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "root": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", "slot": 12 }, "source": { - "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "root": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", "slot": 11 } } @@ -546,8 +546,8 @@ "block": { "slot": 13, "proposerIndex": 1, - "parentRoot": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", - "stateRoot": "0x4f3ed1bd4a625037ebbfdcf9b32a384ca5233b432df7e421f1d09af26d2bd841", + "parentRoot": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", + "stateRoot": "0x7009e11b299137957d24d0bc79ab0221c9a078fc577b02ddf072d23be92111c1", "body": { "attestations": { "data": [] @@ -559,15 +559,15 @@ "data": { "slot": 13, "head": { - "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "root": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", "slot": 13 }, "target": { - "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "root": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", "slot": 13 }, "source": { - "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "root": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", "slot": 12 } } @@ -585,8 +585,8 @@ "block": { "slot": 14, "proposerIndex": 2, - "parentRoot": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", - "stateRoot": "0x638ae62be9308adae58e1239e365a27d053727985b6323652cb66c8a84926e88", + "parentRoot": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", + "stateRoot": "0x9142529b461b68fa191e2d37ff121905c21d05bcebd1c89bcee0119fdf019467", "body": { "attestations": { "data": [] @@ -598,15 +598,15 @@ "data": { "slot": 14, "head": { - "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "root": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", "slot": 14 }, "target": { - "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "root": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", "slot": 14 }, "source": { - "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "root": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", "slot": 13 } } @@ -624,8 +624,8 @@ "block": { "slot": 15, "proposerIndex": 3, - "parentRoot": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", - "stateRoot": "0x0792f723312433e078a11523609eb9b90a961d0ce1346717f86dfb56a09712ca", + "parentRoot": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", + "stateRoot": "0x2badbb1cf8283532ca7d12385a471b9a36e0f9c283ebabb871d7e32cde9e97b4", "body": { "attestations": { "data": [] @@ -637,15 +637,15 @@ "data": { "slot": 15, "head": { - "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "root": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", "slot": 15 }, "target": { - "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "root": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", "slot": 15 }, "source": { - "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "root": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", "slot": 14 } } @@ -663,8 +663,8 @@ "block": { "slot": 16, "proposerIndex": 0, - "parentRoot": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", - "stateRoot": "0x76f71fb8340d28f209436d7f5a46d5ab47f6200ce78e98028869edcc17306c36", + "parentRoot": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", + "stateRoot": "0x3f26419b913372f2471f36726a3ad506221bf90a19b81e966f37f2ed24ce74eb", "body": { "attestations": { "data": [] @@ -676,15 +676,15 @@ "data": { "slot": 16, "head": { - "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "root": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", "slot": 16 }, "target": { - "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "root": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", "slot": 16 }, "source": { - "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "root": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", "slot": 15 } } @@ -702,8 +702,8 @@ "block": { "slot": 17, "proposerIndex": 1, - "parentRoot": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", - "stateRoot": "0x73fb22f47f7645fe9c7484b48946bdcc9feefb77c89119cac4ebfbd897efdf05", + "parentRoot": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", + "stateRoot": "0x1f744250089fe0cf97c1d495f3c023bd1393f03808b7446bc9e12b336fc86552", "body": { "attestations": { "data": [] @@ -715,15 +715,15 @@ "data": { "slot": 17, "head": { - "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "root": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", "slot": 17 }, "target": { - "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "root": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", "slot": 17 }, "source": { - "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "root": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", "slot": 16 } } @@ -741,8 +741,8 @@ "block": { "slot": 18, "proposerIndex": 2, - "parentRoot": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", - "stateRoot": "0x304172e1836eede1f7a460cd0466152ea27449ced669a4e42133579f86d32640", + "parentRoot": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", + "stateRoot": "0x33551af50a0c022f962af04a12cd1cd6dcdccdd4fcc804cdec3380e1d3e5ef02", "body": { "attestations": { "data": [] @@ -754,15 +754,15 @@ "data": { "slot": 18, "head": { - "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "root": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", "slot": 18 }, "target": { - "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "root": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", "slot": 18 }, "source": { - "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "root": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", "slot": 17 } } @@ -780,8 +780,8 @@ "block": { "slot": 19, "proposerIndex": 3, - "parentRoot": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", - "stateRoot": "0xb0bfc653ee2bcbe154796b6f0449d89bb63b090444b361e9a86eaccc74898327", + "parentRoot": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", + "stateRoot": "0xeb08b169c563c8497b5ac6d062c43d6a5d2b150ccd03db8e448bba2cde3fac51", "body": { "attestations": { "data": [] @@ -793,15 +793,15 @@ "data": { "slot": 19, "head": { - "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "root": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", "slot": 19 }, "target": { - "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "root": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", "slot": 19 }, "source": { - "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "root": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", "slot": 18 } } @@ -819,8 +819,8 @@ "block": { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", - "stateRoot": "0x086dd2f62898dc60b1c7a964f30ee820c2fcb855c06aab1db1df6a7bce28690b", + "parentRoot": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", + "stateRoot": "0x35b8931929c034d3d613f57c1ade031f7fdef003251c7a7043cad885e8f1622a", "body": { "attestations": { "data": [] @@ -832,15 +832,15 @@ "data": { "slot": 20, "head": { - "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", + "root": "0x9885f3104db02c971c04daa3540e2ca3a12c589e8856d055d08daaf0ceb1cc31", "slot": 20 }, "target": { - "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", + "root": "0x9885f3104db02c971c04daa3540e2ca3a12c589e8856d055d08daaf0ceb1cc31", "slot": 20 }, "source": { - "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "root": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", "slot": 19 } } @@ -858,8 +858,8 @@ "block": { "slot": 21, "proposerIndex": 1, - "parentRoot": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", - "stateRoot": "0xa1768c44b7eb89500ab88beae409d88b4978914cacbc1e38d9eaf628c9ddd7dd", + "parentRoot": "0x9885f3104db02c971c04daa3540e2ca3a12c589e8856d055d08daaf0ceb1cc31", + "stateRoot": "0x1eefd5f990f349987fedb41510df612249918b42dad460a08b4969efb28f8519", "body": { "attestations": { "data": [] @@ -871,15 +871,15 @@ "data": { "slot": 21, "head": { - "root": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", + "root": "0x4a1849c2952836d9b9a15f73d5f8ac2e6bc2470dd664821d375ae26c9c55232a", "slot": 21 }, "target": { - "root": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", + "root": "0x4a1849c2952836d9b9a15f73d5f8ac2e6bc2470dd664821d375ae26c9c55232a", "slot": 21 }, "source": { - "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", + "root": "0x9885f3104db02c971c04daa3540e2ca3a12c589e8856d055d08daaf0ceb1cc31", "slot": 20 } } @@ -897,8 +897,8 @@ "block": { "slot": 22, "proposerIndex": 2, - "parentRoot": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", - "stateRoot": "0x23d87e0515ea9b2a181553f65b199cce6fb9b99aac4072cef64bc46065f34f6c", + "parentRoot": "0x4a1849c2952836d9b9a15f73d5f8ac2e6bc2470dd664821d375ae26c9c55232a", + "stateRoot": "0x4628fd26ac552f39a2819119e2f55fff356e0c0beff9eb444f660dae030b69e0", "body": { "attestations": { "data": [] @@ -910,15 +910,15 @@ "data": { "slot": 22, "head": { - "root": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", + "root": "0x86e3d786bb9a1191f1c4c29f1d41401f94181f5bbfc0c61f617f58d9afd635ec", "slot": 22 }, "target": { - "root": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", + "root": "0x86e3d786bb9a1191f1c4c29f1d41401f94181f5bbfc0c61f617f58d9afd635ec", "slot": 22 }, "source": { - "root": "0x2c726a2d8605d46d7c8be32a0e333eeb29398a3b0c87606bd8246f27bd2c99bd", + "root": "0x4a1849c2952836d9b9a15f73d5f8ac2e6bc2470dd664821d375ae26c9c55232a", "slot": 21 } } @@ -936,8 +936,8 @@ "block": { "slot": 23, "proposerIndex": 3, - "parentRoot": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", - "stateRoot": "0xd25fb61c44304889bd4c5601f3eee5303207970865fda537ccf0548adf3da872", + "parentRoot": "0x86e3d786bb9a1191f1c4c29f1d41401f94181f5bbfc0c61f617f58d9afd635ec", + "stateRoot": "0x70cf00a8382cbbecc7eda409556d5d2bd7fd9aaa69c536b7788651faa9d71118", "body": { "attestations": { "data": [] @@ -949,15 +949,15 @@ "data": { "slot": 23, "head": { - "root": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", + "root": "0xbaad5776fb3fa1a3a9ee137cd421de631b2bef220111d57288f36465fb2191fa", "slot": 23 }, "target": { - "root": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", + "root": "0xbaad5776fb3fa1a3a9ee137cd421de631b2bef220111d57288f36465fb2191fa", "slot": 23 }, "source": { - "root": "0xe983590785fd0239bc7a01bf5cad9f9bb0a92c826b2f1b3eef9e51d5b8428065", + "root": "0x86e3d786bb9a1191f1c4c29f1d41401f94181f5bbfc0c61f617f58d9afd635ec", "slot": 22 } } @@ -975,8 +975,8 @@ "block": { "slot": 24, "proposerIndex": 0, - "parentRoot": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", - "stateRoot": "0x31b338e157a7afd26a7df8f081fef157a6b87eb41ac241fce2f5e82d96bdd94a", + "parentRoot": "0xbaad5776fb3fa1a3a9ee137cd421de631b2bef220111d57288f36465fb2191fa", + "stateRoot": "0xe58537452b3af4c36070d05171b958a6438a8ccb891d2d48efbb5c2c6fafa08a", "body": { "attestations": { "data": [] @@ -988,15 +988,15 @@ "data": { "slot": 24, "head": { - "root": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", + "root": "0x1f97ba0e3ce5ba4e5a605a0dc22f2470ed02bbe9142766a21615160808ad2aeb", "slot": 24 }, "target": { - "root": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", + "root": "0x1f97ba0e3ce5ba4e5a605a0dc22f2470ed02bbe9142766a21615160808ad2aeb", "slot": 24 }, "source": { - "root": "0x3ebbafb1d328280f6addc96c4a8e5f8610acf2067fabdd56a4335dd72a7754f2", + "root": "0xbaad5776fb3fa1a3a9ee137cd421de631b2bef220111d57288f36465fb2191fa", "slot": 23 } } @@ -1014,8 +1014,8 @@ "block": { "slot": 25, "proposerIndex": 1, - "parentRoot": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", - "stateRoot": "0x4838ec855755beaad6a75b042b07e625b711dc73f3db3f6a7aa143d78aa96fe7", + "parentRoot": "0x1f97ba0e3ce5ba4e5a605a0dc22f2470ed02bbe9142766a21615160808ad2aeb", + "stateRoot": "0x99ba66f91c66dac399de3f43018cf0592e06f55f6ff1f27edb0607cb75ee00be", "body": { "attestations": { "data": [] @@ -1027,15 +1027,15 @@ "data": { "slot": 25, "head": { - "root": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", + "root": "0xd3850629b14ffa193acdc8c29202dff1c748c704bb63813763e588b06e8e9c99", "slot": 25 }, "target": { - "root": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", + "root": "0xd3850629b14ffa193acdc8c29202dff1c748c704bb63813763e588b06e8e9c99", "slot": 25 }, "source": { - "root": "0x497db9491f1aaeee290508bc8f1feb96ba556d1cff1827b14a4381c8bbaab780", + "root": "0x1f97ba0e3ce5ba4e5a605a0dc22f2470ed02bbe9142766a21615160808ad2aeb", "slot": 24 } } @@ -1053,8 +1053,8 @@ "block": { "slot": 26, "proposerIndex": 2, - "parentRoot": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", - "stateRoot": "0x1116129ecbc1b85d65b48b8a94a3242d16c702be62f48737dc42003c937e20b9", + "parentRoot": "0xd3850629b14ffa193acdc8c29202dff1c748c704bb63813763e588b06e8e9c99", + "stateRoot": "0x1f98fe7b3deca215f9dc950b16ca454e110ef4e168ef6fd62ac43599046c683a", "body": { "attestations": { "data": [] @@ -1066,15 +1066,15 @@ "data": { "slot": 26, "head": { - "root": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", + "root": "0x7c67f5f2c796c8f137f4c8b9614a99495acedfef714045473039e68d8c05d8ba", "slot": 26 }, "target": { - "root": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", + "root": "0x7c67f5f2c796c8f137f4c8b9614a99495acedfef714045473039e68d8c05d8ba", "slot": 26 }, "source": { - "root": "0xef20e458410fdda5624925fc27acfa29f35989e5b0fd24a5d6a295b1371f1dba", + "root": "0xd3850629b14ffa193acdc8c29202dff1c748c704bb63813763e588b06e8e9c99", "slot": 25 } } @@ -1092,8 +1092,8 @@ "block": { "slot": 27, "proposerIndex": 3, - "parentRoot": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", - "stateRoot": "0x2457da3db79e0651bd7742fa8177ffb0b79337912b5aa1995d7f27137bfba52e", + "parentRoot": "0x7c67f5f2c796c8f137f4c8b9614a99495acedfef714045473039e68d8c05d8ba", + "stateRoot": "0xc7617fe85a84d087f4d54d379fdc5a7b361f426289f24fff17c22791dd1b6ea3", "body": { "attestations": { "data": [] @@ -1105,15 +1105,15 @@ "data": { "slot": 27, "head": { - "root": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", + "root": "0x467f31bcf8bd2e24437c8ababec787bb04c2643d51019029726c00a399c6c8bd", "slot": 27 }, "target": { - "root": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", + "root": "0x467f31bcf8bd2e24437c8ababec787bb04c2643d51019029726c00a399c6c8bd", "slot": 27 }, "source": { - "root": "0x859c5c71bcd6a693ceae8c1eb0bd49ceca15bc8b7a015c397dc87f370af6ddb9", + "root": "0x7c67f5f2c796c8f137f4c8b9614a99495acedfef714045473039e68d8c05d8ba", "slot": 26 } } @@ -1131,8 +1131,8 @@ "block": { "slot": 28, "proposerIndex": 0, - "parentRoot": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", - "stateRoot": "0xd8593412bfbbf87faa4d6dcead949ab424ed592c51fe3b446013b880da50e72f", + "parentRoot": "0x467f31bcf8bd2e24437c8ababec787bb04c2643d51019029726c00a399c6c8bd", + "stateRoot": "0xe6c04539618af6380da259ced40f1adbabb733d98ccc2ec1688bfe2fbe298a62", "body": { "attestations": { "data": [] @@ -1144,15 +1144,15 @@ "data": { "slot": 28, "head": { - "root": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", + "root": "0xe8d6bcad8d63689b97fcdf3459e144b142b0fd49e9b5850b9c2bb16db1798066", "slot": 28 }, "target": { - "root": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", + "root": "0xe8d6bcad8d63689b97fcdf3459e144b142b0fd49e9b5850b9c2bb16db1798066", "slot": 28 }, "source": { - "root": "0x423b5cd9377d3852a409ced54c3a855284ca124158a2f003f0af47ede699a1ae", + "root": "0x467f31bcf8bd2e24437c8ababec787bb04c2643d51019029726c00a399c6c8bd", "slot": 27 } } @@ -1170,8 +1170,8 @@ "block": { "slot": 29, "proposerIndex": 1, - "parentRoot": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", - "stateRoot": "0x77f20b52268782c3c117132e19c66e880a3fa137aadc78aa8cb37177a954a4da", + "parentRoot": "0xe8d6bcad8d63689b97fcdf3459e144b142b0fd49e9b5850b9c2bb16db1798066", + "stateRoot": "0x930fed612d44c6f992127e448d6732b04976c735c42ed8e5cd7b73ab9f5e77c0", "body": { "attestations": { "data": [] @@ -1183,15 +1183,15 @@ "data": { "slot": 29, "head": { - "root": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", + "root": "0xf32bc2d839bb94487a497fe7e8be2ee66fed628c9a0cab4c0a8a09172d10e91a", "slot": 29 }, "target": { - "root": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", + "root": "0xf32bc2d839bb94487a497fe7e8be2ee66fed628c9a0cab4c0a8a09172d10e91a", "slot": 29 }, "source": { - "root": "0x51840e53d7d4d026c3d7ee2468fe117bf997abebd28c0107ceaf8fd1655fe642", + "root": "0xe8d6bcad8d63689b97fcdf3459e144b142b0fd49e9b5850b9c2bb16db1798066", "slot": 28 } } @@ -1209,8 +1209,8 @@ "block": { "slot": 30, "proposerIndex": 2, - "parentRoot": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", - "stateRoot": "0xe953c85587e056ff27213e9f8bcebfc2fef4aa1351a9b09a87495b57055f4bf4", + "parentRoot": "0xf32bc2d839bb94487a497fe7e8be2ee66fed628c9a0cab4c0a8a09172d10e91a", + "stateRoot": "0xcb0a6571287556560b38458799a0f2349b1b393810ee161c116462337e700872", "body": { "attestations": { "data": [] @@ -1222,15 +1222,15 @@ "data": { "slot": 30, "head": { - "root": "0xdd596d1757fa73928246f105850f3777c7859f83ee0156ab5f53c47ea023c179", + "root": "0x673fd3ca42eb17aaf2326e1916be49ec99afffaa6a9e1c3b9d34dddfc3ad15ee", "slot": 30 }, "target": { - "root": "0xdd596d1757fa73928246f105850f3777c7859f83ee0156ab5f53c47ea023c179", + "root": "0x673fd3ca42eb17aaf2326e1916be49ec99afffaa6a9e1c3b9d34dddfc3ad15ee", "slot": 30 }, "source": { - "root": "0x41eb95d028cb061f28160a33c68f37bf0ed5330182254810d036b8c96940363b", + "root": "0xf32bc2d839bb94487a497fe7e8be2ee66fed628c9a0cab4c0a8a09172d10e91a", "slot": 29 } } @@ -1240,7 +1240,7 @@ ], "maxSlot": 30, "_info": { - "hash": "0x087bb9195d9b77f7b684f8dcadd9c2b2db8f156eb9c7dd4853101aff46b5a42e", + "hash": "0x7c8c29c2077f7e8c15f316b2e9f1732ad2a47b0f511e986f965cddbd7ee9bfb8", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_justifiable_constraint[fork_Devnet]", "description": "Attestation target advances while respecting justifiability rules.\n\n Scenario\n --------\n Build a 10-slot chain and observe how the attestation target advances\n over time while remaining justifiable relative to genesis (finalized at slot 0).\n\n Justifiability Rules (see Slot.is_justifiable_after)\n -----------------------------------------------------\n\n The target starts from current head and looks back at most 3 slots towards safe target.\n\n Then, a slot is deemed justifiable at distance delta from finalization if:\n 1. delta \u2264 5\n 2. delta is a perfect square (1, 4, 9, 16, 25, ...)\n 3. delta is a pronic number (2, 6, 12, 20, 30, ...)\n\n Why This Matters\n ----------------\n The justifiability rules prevent long-range attacks by restricting which\n checkpoints validators can attest to. The mathematical pattern (perfect squares\n and pronic numbers) creates increasingly sparse justifiable slots as the chain\n grows beyond finalization, providing security guarantees.\n\n The test verifies that the target selection algorithm respects these rules\n and never selects a non-justifiable target.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json index 39c7225..2f2db34 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_extended_chain[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -78,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -91,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -117,8 +117,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -130,15 +130,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -156,8 +156,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -169,15 +169,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -195,8 +195,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -208,15 +208,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -234,8 +234,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -247,15 +247,15 @@ "data": { "slot": 5, "head": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "target": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "source": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 } } @@ -273,8 +273,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", - "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", + "parentRoot": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", + "stateRoot": "0x3ccb488b064dae8643040852e10acc9276aa087d85bb82387eb29101245d1d35", "body": { "attestations": { "data": [] @@ -286,15 +286,15 @@ "data": { "slot": 6, "head": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "target": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "source": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 } } @@ -312,8 +312,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", - "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", + "parentRoot": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", + "stateRoot": "0x924c49d73043292db24525a72614bc79453028c75a9fb23b27a0e4e557331560", "body": { "attestations": { "data": [] @@ -325,15 +325,15 @@ "data": { "slot": 7, "head": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "target": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "source": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 } } @@ -351,8 +351,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", - "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", + "parentRoot": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", + "stateRoot": "0xcf16ef20643079955b1ee11f6e73ad0694d6c0a2f97d6732e43dc342d6277bbb", "body": { "attestations": { "data": [] @@ -364,15 +364,15 @@ "data": { "slot": 8, "head": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "target": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "source": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 } } @@ -382,7 +382,7 @@ ], "maxSlot": 8, "_info": { - "hash": "0x32bba858f364641581f0b5fb55efa07e5f08f6e26e40673fad74bc4f52abd08e", + "hash": "0xccc60d324f44c015f7d3d4585c42c7563b4515017776acd163278d22a8e8b5e1", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_extended_chain[fork_Devnet]", "description": "Attestation target advances progressively over extended chain.\n\n Scenario\n --------\n Build a longer chain (slots 1-8) observing target advancement pattern.\n\n Expected:\n - Initial slots: target at genesis (conservative)\n - Middle slots: target advances to slot 1\n - Target advances gradually, not jumping to head\n\n Why This Matters\n ----------------\n Over extended chains, the target selection should show smooth,\n gradual advancement as attestation weight accumulates.\n\n The target lags behind the head, providing a stable reference point that\n advances only when sufficient consensus has formed. This prevents validators\n from attesting too far ahead without adequate safety guarantees.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json index 424e766..5d190f1 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_slot_gaps[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -78,8 +78,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -91,15 +91,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -117,8 +117,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0x7031a4d5385dcfbe2a9f373845bb8ffd86863aed0d0d87976141c9b11edac5bc", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0x9f9e08828769c720e902e34d8e6c190c92f88856cfdc679df5c46cbbf8b27d4e", "body": { "attestations": { "data": [] @@ -130,15 +130,15 @@ "data": { "slot": 3, "head": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 }, "target": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -156,8 +156,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", - "stateRoot": "0x1ee1961b8157e69e77990196fbab902d16c0f51bd8da1848dfa0a6d8e177ad7c", + "parentRoot": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", + "stateRoot": "0x0276d5a3164729b2862239cd9bb33115ba5ab902bcf0c67c7b2130909d1915a1", "body": { "attestations": { "data": [] @@ -169,15 +169,15 @@ "data": { "slot": 5, "head": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 }, "target": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 }, "source": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 } } @@ -187,7 +187,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0x601d371d2e0b5167a607bdf93cd3a2559623711e09ab8d8d7f17afdfd51cb991", + "hash": "0x3c5e09193b89f78bfbe3f1829a0e67edef275c0c44d2514da6e5400bd3c5b958", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_slot_gaps[fork_Devnet]", "description": "Attestation target handles missed slots correctly.\n\n Scenario\n --------\n Process blocks at slots 1, 3, 5 (skipping even slots).\n\n Expected:\n - Targets advance despite gaps\n - Targets remain justifiable\n - Safe target stays valid\n\n Why This Matters\n ----------------\n Missed slots are common when proposers fail or network partitions occur.\n\n The target selection must handle sparse block production gracefully,\n ensuring validators can still make progress even with gaps in the chain.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json index 5d5d2a7..5d27ba7 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_advances_through_deep_chain[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -77,8 +77,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -90,15 +90,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -115,8 +115,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -128,15 +128,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,8 +153,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -166,15 +166,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -191,8 +191,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -204,15 +204,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -229,8 +229,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -242,15 +242,15 @@ "data": { "slot": 5, "head": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "target": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "source": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 } } @@ -267,8 +267,8 @@ "block": { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", - "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", + "parentRoot": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", + "stateRoot": "0x3ccb488b064dae8643040852e10acc9276aa087d85bb82387eb29101245d1d35", "body": { "attestations": { "data": [] @@ -280,15 +280,15 @@ "data": { "slot": 6, "head": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "target": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 }, "source": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 } } @@ -305,8 +305,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", - "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", + "parentRoot": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", + "stateRoot": "0x924c49d73043292db24525a72614bc79453028c75a9fb23b27a0e4e557331560", "body": { "attestations": { "data": [] @@ -318,15 +318,15 @@ "data": { "slot": 7, "head": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "target": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 }, "source": { - "root": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", + "root": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", "slot": 6 } } @@ -343,8 +343,8 @@ "block": { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", - "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", + "parentRoot": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", + "stateRoot": "0xcf16ef20643079955b1ee11f6e73ad0694d6c0a2f97d6732e43dc342d6277bbb", "body": { "attestations": { "data": [] @@ -356,15 +356,15 @@ "data": { "slot": 8, "head": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "target": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 }, "source": { - "root": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", + "root": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", "slot": 7 } } @@ -381,8 +381,8 @@ "block": { "slot": 9, "proposerIndex": 1, - "parentRoot": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", - "stateRoot": "0xc8f2880bc8e3d1ba655c75f34ae5a20555503ce8296cbc3a2a266fbcdb7b078b", + "parentRoot": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", + "stateRoot": "0x4a76b547a30d61443a8e045ef40912658582ba64fb8c6d483c207f8f7a55f063", "body": { "attestations": { "data": [] @@ -394,15 +394,15 @@ "data": { "slot": 9, "head": { - "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "root": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", "slot": 9 }, "target": { - "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "root": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", "slot": 9 }, "source": { - "root": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", + "root": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", "slot": 8 } } @@ -419,8 +419,8 @@ "block": { "slot": 10, "proposerIndex": 2, - "parentRoot": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", - "stateRoot": "0x054571db4eb9706f6f29a97bffaa00765b2a75b77ad5812e8619066e942d3192", + "parentRoot": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", + "stateRoot": "0xd424ce4fe3a44ae13b83ebefe1efaacf805e00dd4ca5839ce2d0788de4936864", "body": { "attestations": { "data": [] @@ -432,15 +432,15 @@ "data": { "slot": 10, "head": { - "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "root": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", "slot": 10 }, "target": { - "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "root": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", "slot": 10 }, "source": { - "root": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", + "root": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", "slot": 9 } } @@ -457,8 +457,8 @@ "block": { "slot": 11, "proposerIndex": 3, - "parentRoot": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", - "stateRoot": "0x4a359bc7767356dd13c761cc98bd9f0a62013636c0395e9babd5d37794c9c728", + "parentRoot": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", + "stateRoot": "0x5bd597860d3f6c089f438b5ae3aebcb1858bb0dd74c09ea84c6f68f4dc1f4880", "body": { "attestations": { "data": [] @@ -470,15 +470,15 @@ "data": { "slot": 11, "head": { - "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "root": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", "slot": 11 }, "target": { - "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "root": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", "slot": 11 }, "source": { - "root": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", + "root": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", "slot": 10 } } @@ -495,8 +495,8 @@ "block": { "slot": 12, "proposerIndex": 0, - "parentRoot": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", - "stateRoot": "0x584b4ae5faaeb8a0cc6b190b4f570d773fc3f40426c0eb5a1f1730b199633b0a", + "parentRoot": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", + "stateRoot": "0x2cd6d0d82adf90b137c6f4ba447cd7be99e22ee890568024ba9b51a41cef58e2", "body": { "attestations": { "data": [] @@ -508,15 +508,15 @@ "data": { "slot": 12, "head": { - "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "root": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", "slot": 12 }, "target": { - "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "root": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", "slot": 12 }, "source": { - "root": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", + "root": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", "slot": 11 } } @@ -533,8 +533,8 @@ "block": { "slot": 13, "proposerIndex": 1, - "parentRoot": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", - "stateRoot": "0x4f3ed1bd4a625037ebbfdcf9b32a384ca5233b432df7e421f1d09af26d2bd841", + "parentRoot": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", + "stateRoot": "0x7009e11b299137957d24d0bc79ab0221c9a078fc577b02ddf072d23be92111c1", "body": { "attestations": { "data": [] @@ -546,15 +546,15 @@ "data": { "slot": 13, "head": { - "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "root": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", "slot": 13 }, "target": { - "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "root": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", "slot": 13 }, "source": { - "root": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", + "root": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", "slot": 12 } } @@ -571,8 +571,8 @@ "block": { "slot": 14, "proposerIndex": 2, - "parentRoot": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", - "stateRoot": "0x638ae62be9308adae58e1239e365a27d053727985b6323652cb66c8a84926e88", + "parentRoot": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", + "stateRoot": "0x9142529b461b68fa191e2d37ff121905c21d05bcebd1c89bcee0119fdf019467", "body": { "attestations": { "data": [] @@ -584,15 +584,15 @@ "data": { "slot": 14, "head": { - "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "root": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", "slot": 14 }, "target": { - "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "root": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", "slot": 14 }, "source": { - "root": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", + "root": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", "slot": 13 } } @@ -609,8 +609,8 @@ "block": { "slot": 15, "proposerIndex": 3, - "parentRoot": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", - "stateRoot": "0x0792f723312433e078a11523609eb9b90a961d0ce1346717f86dfb56a09712ca", + "parentRoot": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", + "stateRoot": "0x2badbb1cf8283532ca7d12385a471b9a36e0f9c283ebabb871d7e32cde9e97b4", "body": { "attestations": { "data": [] @@ -622,15 +622,15 @@ "data": { "slot": 15, "head": { - "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "root": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", "slot": 15 }, "target": { - "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "root": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", "slot": 15 }, "source": { - "root": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", + "root": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", "slot": 14 } } @@ -647,8 +647,8 @@ "block": { "slot": 16, "proposerIndex": 0, - "parentRoot": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", - "stateRoot": "0x76f71fb8340d28f209436d7f5a46d5ab47f6200ce78e98028869edcc17306c36", + "parentRoot": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", + "stateRoot": "0x3f26419b913372f2471f36726a3ad506221bf90a19b81e966f37f2ed24ce74eb", "body": { "attestations": { "data": [] @@ -660,15 +660,15 @@ "data": { "slot": 16, "head": { - "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "root": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", "slot": 16 }, "target": { - "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "root": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", "slot": 16 }, "source": { - "root": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", + "root": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", "slot": 15 } } @@ -685,8 +685,8 @@ "block": { "slot": 17, "proposerIndex": 1, - "parentRoot": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", - "stateRoot": "0x73fb22f47f7645fe9c7484b48946bdcc9feefb77c89119cac4ebfbd897efdf05", + "parentRoot": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", + "stateRoot": "0x1f744250089fe0cf97c1d495f3c023bd1393f03808b7446bc9e12b336fc86552", "body": { "attestations": { "data": [] @@ -698,15 +698,15 @@ "data": { "slot": 17, "head": { - "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "root": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", "slot": 17 }, "target": { - "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "root": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", "slot": 17 }, "source": { - "root": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", + "root": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", "slot": 16 } } @@ -723,8 +723,8 @@ "block": { "slot": 18, "proposerIndex": 2, - "parentRoot": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", - "stateRoot": "0x304172e1836eede1f7a460cd0466152ea27449ced669a4e42133579f86d32640", + "parentRoot": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", + "stateRoot": "0x33551af50a0c022f962af04a12cd1cd6dcdccdd4fcc804cdec3380e1d3e5ef02", "body": { "attestations": { "data": [] @@ -736,15 +736,15 @@ "data": { "slot": 18, "head": { - "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "root": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", "slot": 18 }, "target": { - "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "root": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", "slot": 18 }, "source": { - "root": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", + "root": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", "slot": 17 } } @@ -761,8 +761,8 @@ "block": { "slot": 19, "proposerIndex": 3, - "parentRoot": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", - "stateRoot": "0xb0bfc653ee2bcbe154796b6f0449d89bb63b090444b361e9a86eaccc74898327", + "parentRoot": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", + "stateRoot": "0xeb08b169c563c8497b5ac6d062c43d6a5d2b150ccd03db8e448bba2cde3fac51", "body": { "attestations": { "data": [] @@ -774,15 +774,15 @@ "data": { "slot": 19, "head": { - "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "root": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", "slot": 19 }, "target": { - "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "root": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", "slot": 19 }, "source": { - "root": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", + "root": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", "slot": 18 } } @@ -793,7 +793,7 @@ "valid": true, "checks": { "headSlot": 20, - "headRoot": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", + "headRoot": "0x9885f3104db02c971c04daa3540e2ca3a12c589e8856d055d08daaf0ceb1cc31", "headRootLabel": "block_20" }, "stepType": "block", @@ -801,8 +801,8 @@ "block": { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", - "stateRoot": "0x086dd2f62898dc60b1c7a964f30ee820c2fcb855c06aab1db1df6a7bce28690b", + "parentRoot": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", + "stateRoot": "0x35b8931929c034d3d613f57c1ade031f7fdef003251c7a7043cad885e8f1622a", "body": { "attestations": { "data": [] @@ -814,15 +814,15 @@ "data": { "slot": 20, "head": { - "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", + "root": "0x9885f3104db02c971c04daa3540e2ca3a12c589e8856d055d08daaf0ceb1cc31", "slot": 20 }, "target": { - "root": "0xc0a0053d73e322aaa3cc0b3c5124d6ea5cf6eebbebc950814fec2612efeb2b82", + "root": "0x9885f3104db02c971c04daa3540e2ca3a12c589e8856d055d08daaf0ceb1cc31", "slot": 20 }, "source": { - "root": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", + "root": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", "slot": 19 } } @@ -833,7 +833,7 @@ ], "maxSlot": 20, "_info": { - "hash": "0x5ad4414ae62a84ccc1d0b107e7305210bfef592d54e97ea73cf07fc40d551e9e", + "hash": "0xbfc7c8a714f454ddfcf28aaf49e16c9e4b9f90f0de78f667f11826a9cd4e567f", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_advances_through_deep_chain[fork_Devnet]", "description": "Fork choice head advances through a deep chain correctly.\n\n Scenario\n --------\n Build a long chain (slots 1-20) and verify head reaches the end.\n\n Expected Behavior:\n - Head advances through all 20 blocks\n - Final head = slot 20\n - Fork choice scales to longer chains\n\n Why This Matters\n ----------------\n This tests that the fork choice algorithm scales to longer chains and\n correctly handles the tree-walking logic through many blocks.\n\n Real networks have chains thousands of blocks long. The algorithm must:\n - Efficiently traverse deep trees\n - Maintain correct head even with many ancestors\n - Not degrade in performance or correctness with depth\n\n A 20-block chain is a modest test of this scalability.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json index b014d09..cf03053 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_switches_to_heavier_fork[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -71,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "headRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "headRootLabel": "common" }, "stepType": "block", @@ -79,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -92,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -112,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a" }, "stepType": "block", @@ -120,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -133,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a" }, "stepType": "block", @@ -161,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -174,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -194,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -202,8 +202,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -215,15 +215,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -234,7 +234,7 @@ ], "maxSlot": 3, "_info": { - "hash": "0x3cd680ce8974c9c4bfaefeeb90632310c9c67099d2f8eba5a0b04addccdd4c43", + "hash": "0xaaf4d3533d5d07ff419462a6ec9acd98edd8e3cf1e26f3505489fc52d6d5467f", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_switches_to_heavier_fork[fork_Devnet]", "description": "Fork choice head switches when a competing fork becomes heavier.\n\n Scenario\n --------\n Create two forks at slot 2, then extend one fork to make it heavier.\n\n Expected Behavior:\n - After fork A (slot 2): head = fork A\n - After fork B (slot 2): head = still fork A (tie-breaker)\n - After extending fork B (slot 3): head = slot 3 (fork B wins!)\n\n Why This Matters\n ----------------\n This demonstrates the core LMD-GHOST property: the head follows the heaviest\n subtree. When fork B is extended with a child block, that child's proposer\n implicitly attests to fork B, giving it more weight.\n\n Fork choice recognizes this weight increase and switches the head to fork B's\n descendant. This is how the protocol reaches consensus - validators converge\n on the fork with the most support (weight).\n\n This is also how reorgs happen: a previously non-canonical fork can become\n canonical if it gains more attestation weight.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json index af448e2..5cbfad8 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_deep_fork_split[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -71,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "headRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "headRootLabel": "common" }, "stepType": "block", @@ -79,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -92,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -112,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -120,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -133,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,7 +153,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -161,8 +161,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -174,15 +174,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -194,7 +194,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "headRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -202,8 +202,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -215,15 +215,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -235,7 +235,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "headRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -243,8 +243,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -256,15 +256,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -276,7 +276,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "headRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -284,8 +284,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -297,15 +297,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -317,7 +317,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "headRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -325,8 +325,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -338,15 +338,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -358,7 +358,7 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "headRoot": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "headRootLabel": "fork_b_5" }, "stepType": "block", @@ -366,8 +366,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -379,15 +379,15 @@ "data": { "slot": 5, "head": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "target": { - "root": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", + "root": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", "slot": 5 }, "source": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 } } @@ -398,7 +398,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0x6ae81fc5748a34ca376abfec596672a8dbc915b10affa5662bb555b4ca957d15", + "hash": "0x04cab0a7de2cb20b31ad58fe67088cfd813fbd79164b2ff538bd8bfeb23c199b", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_deep_fork_split[fork_Devnet]", "description": "Fork choice handles deep fork splits correctly.\n\n Scenario\n --------\n Create two forks that diverge at slot 2 and extend to different depths.\n\n Expected Behavior:\n - Fork A extends to slot 4\n - Fork B extends to slot 5\n - Head follows the longer (heavier) fork B\n\n Why This Matters\n ----------------\n In practice, forks can persist for multiple slots before one gains dominance.\n This tests that fork choice correctly follows the deeper fork, which has\n accumulated more proposer attestations along its chain.\n\n Each block in a fork adds weight from its proposer's attestation. A longer\n fork has more accumulated weight from the proposers along its length.\n\n This is how the protocol ensures liveness: the chain that continues to grow\n (accumulating blocks and attestations) becomes the canonical chain.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json index 08f0fba..7a09942 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_gaps_in_slots[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -77,8 +77,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -90,15 +90,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -115,8 +115,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0x7031a4d5385dcfbe2a9f373845bb8ffd86863aed0d0d87976141c9b11edac5bc", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0x9f9e08828769c720e902e34d8e6c190c92f88856cfdc679df5c46cbbf8b27d4e", "body": { "attestations": { "data": [] @@ -128,15 +128,15 @@ "data": { "slot": 3, "head": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 }, "target": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,8 +153,8 @@ "block": { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", - "stateRoot": "0x1ee1961b8157e69e77990196fbab902d16c0f51bd8da1848dfa0a6d8e177ad7c", + "parentRoot": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", + "stateRoot": "0x0276d5a3164729b2862239cd9bb33115ba5ab902bcf0c67c7b2130909d1915a1", "body": { "attestations": { "data": [] @@ -166,15 +166,15 @@ "data": { "slot": 5, "head": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 }, "target": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 }, "source": { - "root": "0xf5542a1fb5359bd605165b77546fe6600221d20b5d7e019ac878f065ed04aeb4", + "root": "0x89cfdc506708d136504d6a99d95fa97657185e6f6e827e11d4761bceb2318828", "slot": 3 } } @@ -191,8 +191,8 @@ "block": { "slot": 7, "proposerIndex": 3, - "parentRoot": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", - "stateRoot": "0x784f0f32d518e8a5ccdc7b0887d26d64b2d0048faa696c0616d8ff5a37d02d28", + "parentRoot": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", + "stateRoot": "0x80615d070ddce2d537ff4b4ce5d403cbb794b0fa1a84d5d4e07b30ae72f4d2e8", "body": { "attestations": { "data": [] @@ -204,15 +204,15 @@ "data": { "slot": 7, "head": { - "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", + "root": "0x2891408b24b855513d0d64726c152225194cdf79d36263b66ff894efb8debbe6", "slot": 7 }, "target": { - "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", + "root": "0x2891408b24b855513d0d64726c152225194cdf79d36263b66ff894efb8debbe6", "slot": 7 }, "source": { - "root": "0x839323e05137106758fd004cee3fd77597c41f120e8547a4890d2c590accfa66", + "root": "0x8dbd8386b54bc6ca8ee819d8459a471df78a5fe80259b714f36921d0c50bfc76", "slot": 5 } } @@ -229,8 +229,8 @@ "block": { "slot": 9, "proposerIndex": 1, - "parentRoot": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", - "stateRoot": "0x2b033d926f0beb3031e0abc960a396f6e4ba10daa94a8b8598fd01a8f2b8c36d", + "parentRoot": "0x2891408b24b855513d0d64726c152225194cdf79d36263b66ff894efb8debbe6", + "stateRoot": "0x651db0949f28b32c320eb3d8ce9ef74d656c19bffec0ea55e73093691d616714", "body": { "attestations": { "data": [] @@ -242,15 +242,15 @@ "data": { "slot": 9, "head": { - "root": "0x70f7a111cd22263e3e02b323dbc47d9c67961ad39159d93a4ed0e4c5efb5b5e3", + "root": "0x94c247be6246338bb128b05e0e725640fe68eff83d6d4bcac8211fb41580e794", "slot": 9 }, "target": { - "root": "0x70f7a111cd22263e3e02b323dbc47d9c67961ad39159d93a4ed0e4c5efb5b5e3", + "root": "0x94c247be6246338bb128b05e0e725640fe68eff83d6d4bcac8211fb41580e794", "slot": 9 }, "source": { - "root": "0x84f7880a851b914e25e0e5d15c0163e79182349ad7f848017845f2b8dcff5343", + "root": "0x2891408b24b855513d0d64726c152225194cdf79d36263b66ff894efb8debbe6", "slot": 7 } } @@ -260,7 +260,7 @@ ], "maxSlot": 9, "_info": { - "hash": "0x9e626b69dcb3336fcc73a16b988aff4a08d107fc0b9542cf0512c7b51bbd7231", + "hash": "0x1c4e74137050dc0c7edf1fd097829cad26332dcb8f90d7981b04881411970625", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_gaps_in_slots[fork_Devnet]", "description": "Fork choice head handles missing slots correctly.\n\n Scenario\n --------\n Build blocks at slots 1, 3, 5, 7, 9 (skipping even slots).\n\n Expected Behavior:\n - Head advances to each present block\n - Skipped slots don't affect fork choice\n - Head correctly identifies the leaf despite gaps\n\n Why This Matters\n ----------------\n Missed slots are common in production:\n - Offline proposers\n - Network partitions\n - Proposer failures\n\n Fork choice must handle sparse block production correctly. The algorithm\n doesn't require consecutive slots - it works with any tree structure where\n gaps are simply missing nodes.\n\n This verifies the algorithm handles real-world conditions where not every\n slot has a block, which is the norm rather than the exception.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json index 54da480..17e905c 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_large_gaps[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -77,8 +77,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -90,15 +90,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -115,8 +115,8 @@ "block": { "slot": 10, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0x1b3e2d27ccd32951999bb5859116a897a055f096276d78601158e22fd2632442", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb53955df5d82a0ab91c702ce71a80fd0bd01e2389339bd2aa9471846352652b2", "body": { "attestations": { "data": [] @@ -128,15 +128,15 @@ "data": { "slot": 10, "head": { - "root": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", + "root": "0x3214067f504a9a4e8be1174138cea0a0983a0b0525e83bda179edf7a649629f9", "slot": 10 }, "target": { - "root": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", + "root": "0x3214067f504a9a4e8be1174138cea0a0983a0b0525e83bda179edf7a649629f9", "slot": 10 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,8 +153,8 @@ "block": { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", - "stateRoot": "0x4918471d37cd4049787f147e83fc9aab2c80a492aba13460a4cde889e301954e", + "parentRoot": "0x3214067f504a9a4e8be1174138cea0a0983a0b0525e83bda179edf7a649629f9", + "stateRoot": "0xb65749592f6f7f5fc7e1163810ba9406c143d1752bde91d95703887876a5b1eb", "body": { "attestations": { "data": [] @@ -166,15 +166,15 @@ "data": { "slot": 20, "head": { - "root": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", + "root": "0x29c64dcaeddd7f5dc88be80f8272d0eed5efb860cb04dbab22531eb9409cd5d2", "slot": 20 }, "target": { - "root": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", + "root": "0x29c64dcaeddd7f5dc88be80f8272d0eed5efb860cb04dbab22531eb9409cd5d2", "slot": 20 }, "source": { - "root": "0x908b27489ac9909404659a5e90766c65fdae31e9de67441c48361c38e1674ab3", + "root": "0x3214067f504a9a4e8be1174138cea0a0983a0b0525e83bda179edf7a649629f9", "slot": 10 } } @@ -191,8 +191,8 @@ "block": { "slot": 30, "proposerIndex": 2, - "parentRoot": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", - "stateRoot": "0x2cd0120e789b59cb2e0a8ac5204249b289898918714582cc034686cb75801d22", + "parentRoot": "0x29c64dcaeddd7f5dc88be80f8272d0eed5efb860cb04dbab22531eb9409cd5d2", + "stateRoot": "0xed5e9547e0f7e3c4a1441d88c351ad454bb03973a79501d89e7d17ce93c222c5", "body": { "attestations": { "data": [] @@ -204,15 +204,15 @@ "data": { "slot": 30, "head": { - "root": "0x1a6568b59c71bea3297711437ce946b615837efcf65443286ec3a3cbcddc83c3", + "root": "0xec48757d755c4a71a2b4e57c6343ccbea6a8c922690bac59fe9df04dd6e93eff", "slot": 30 }, "target": { - "root": "0x1a6568b59c71bea3297711437ce946b615837efcf65443286ec3a3cbcddc83c3", + "root": "0xec48757d755c4a71a2b4e57c6343ccbea6a8c922690bac59fe9df04dd6e93eff", "slot": 30 }, "source": { - "root": "0x243353f4b9e4289be816a3ef706c0689adddf2559fa5050e7e7a45dc5dabcd5e", + "root": "0x29c64dcaeddd7f5dc88be80f8272d0eed5efb860cb04dbab22531eb9409cd5d2", "slot": 20 } } @@ -222,7 +222,7 @@ ], "maxSlot": 30, "_info": { - "hash": "0x75234ae4514adf94fbfd2f336070fdcbd98111f59920b4ada865446b8fd6d008", + "hash": "0x3535bcb8a480fe5f76ad58fcb8aa539c082838ef70fa57b9f7f92ada56f0520b", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_large_gaps[fork_Devnet]", "description": "Fork choice head handles large gaps between blocks.\n\n Scenario\n --------\n Build blocks at slots 1, 10, 20, 30 (gaps of 9-10 slots).\n\n Expected Behavior:\n - Head advances despite large gaps\n - Fork choice is gap-size independent\n - Head reaches the furthest block\n\n Why This Matters\n ----------------\n Large gaps can occur during:\n - Extended network partitions\n - Chain reorganizations\n - Periods of high validator downtime\n - Initial sync after being offline\n\n The fork choice algorithm must remain correct regardless of gap size.\n Distance between blocks should not affect the correctness of head selection -\n only the tree structure matters.\n\n This test verifies that even with dramatic gaps (representing severe network\n conditions), fork choice still identifies the correct head.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json index 69184af..e55e9e2 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_two_competing_forks[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -71,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "headRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "headRootLabel": "common" }, "stepType": "block", @@ -79,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -92,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -112,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a" }, "stepType": "block", @@ -120,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -133,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a" }, "stepType": "block", @@ -161,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -174,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -193,7 +193,7 @@ ], "maxSlot": 2, "_info": { - "hash": "0x445ea1d1d46f1a51804be76a2127589d3af23623d2105b4cfbf13d94032c85b8", + "hash": "0x4bc2b45ff4760cad0fd489f241759c5452e550dca1f4cab25d47818c5156cc69", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_two_competing_forks[fork_Devnet]", "description": "Fork choice selects head when two forks compete at the same slot.\n\n Scenario\n --------\n Create two competing blocks at slot 2, both building on slot 1.\n\n Expected Behavior:\n - After slot 1: head = slot 1 (common ancestor)\n - After fork A (slot 2): head = slot 2 (fork A, first seen)\n - After fork B (slot 2): head = slot 2 (still fork A)\n - Both forks have equal weight (1 proposer attestation each)\n - Head breaks tie lexicographically by block root\n\n Why This Matters\n ----------------\n This is an important fork choice scenario: two blocks competing for the\n same slot. Even with equal attestation weight, fork choice must deterministically\n select a head.\n\n The algorithm uses lexicographic order of block roots as a tie-breaker,\n ensuring all nodes agree on the same head even when forks have equal weight.\n\n This prevents network splits and ensures consensus converges.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json index b38a880..a185a6c 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_back_and_forth_reorg_oscillation[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,27 +31,27 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 }, { - "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "pubkey": "0x3fa3003fd5c7ab54f1a034621281922447676905d22c850e0cca0d09148f7c029f7feb371349fe2e71930249e9bc2d5198c9903f", "index": 4 }, { - "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "pubkey": "0x4f0dfc5ac80e1109e571b06936d7f85f8bd1bb3f3fc51838f54e1d176f1298593d415e24c0a6873775eacd7c2757ca5f1cb8f176", "index": 5 } ] @@ -67,7 +67,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe483b5237726c89bec17cd6aa4dd397be5b4952d73e7e79d4c4c40b88734227e", + "stateRoot": "0x8f263e7d66474f01517ba675e376c44a5c1520809de0d273a93148f21c07a1ac", "body": { "attestations": { "data": [] @@ -79,7 +79,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "headRoot": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "headRootLabel": "base" }, "stepType": "block", @@ -87,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", - "stateRoot": "0x6f64b6379a51d5ea3a51eee1dde0e2457e89942177a60a0705ad295acadf7674", + "parentRoot": "0xa7e0a4699554aefbc06aaf7738da9b04e918748fc41fc567c7a1965fe606748b", + "stateRoot": "0x5cfea33d66f51dc81d89df62ddb734fe8478cd73bf7a4e001a8d8da87b58af51", "body": { "attestations": { "data": [] @@ -100,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 }, "target": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 }, "source": { - "root": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", + "root": "0xa7e0a4699554aefbc06aaf7738da9b04e918748fc41fc567c7a1965fe606748b", "slot": 0 } } @@ -120,7 +120,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "headRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -128,8 +128,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", - "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", + "parentRoot": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", + "stateRoot": "0x910714a078b25da9d19832576fca877a6f250698728096a676bb56cf0cc23e34", "body": { "attestations": { "data": [] @@ -141,15 +141,15 @@ "data": { "slot": 2, "head": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "target": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "source": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 } } @@ -161,7 +161,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "headRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -169,8 +169,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", - "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", + "parentRoot": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", + "stateRoot": "0x910714a078b25da9d19832576fca877a6f250698728096a676bb56cf0cc23e34", "body": { "attestations": { "data": [] @@ -182,15 +182,15 @@ "data": { "slot": 2, "head": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "target": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "source": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 } } @@ -202,7 +202,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "headRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -210,8 +210,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", - "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", + "parentRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", + "stateRoot": "0x52b8332a0e1e578d164574770d9158c14fd68240f5032157ceeea26efa69c4f5", "body": { "attestations": { "data": [] @@ -223,15 +223,15 @@ "data": { "slot": 3, "head": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "target": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "source": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 } } @@ -243,7 +243,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "headRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -251,8 +251,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", - "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", + "parentRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", + "stateRoot": "0x52b8332a0e1e578d164574770d9158c14fd68240f5032157ceeea26efa69c4f5", "body": { "attestations": { "data": [] @@ -264,15 +264,15 @@ "data": { "slot": 3, "head": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "target": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "source": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 } } @@ -284,7 +284,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "headRoot": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -292,8 +292,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", - "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", + "parentRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", + "stateRoot": "0x7c5b159a6fae4548ca6ea2c26670497de1c0b67e8fcab3ced5a1bfb63b7d3339", "body": { "attestations": { "data": [] @@ -305,15 +305,15 @@ "data": { "slot": 4, "head": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "target": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "source": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 } } @@ -325,7 +325,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "headRoot": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -333,8 +333,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", - "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", + "parentRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", + "stateRoot": "0x7c5b159a6fae4548ca6ea2c26670497de1c0b67e8fcab3ced5a1bfb63b7d3339", "body": { "attestations": { "data": [] @@ -346,15 +346,15 @@ "data": { "slot": 4, "head": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "target": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "source": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 } } @@ -366,7 +366,7 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", + "headRoot": "0x67dafaeeb64b2323fa08e3019ea7c341d99d554e77320667d366a105dff9428f", "headRootLabel": "fork_b_5" }, "stepType": "block", @@ -374,8 +374,8 @@ "block": { "slot": 5, "proposerIndex": 5, - "parentRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", - "stateRoot": "0xa2b05e5ab5824c023fcfce5f1e9276d95c2c034ed07721d059d5ad2c07007d99", + "parentRoot": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", + "stateRoot": "0xd312ce21c03aa35d8db5e0b373b3e4b59af5a474a09a5332aaaf37a59e85a75c", "body": { "attestations": { "data": [] @@ -387,15 +387,15 @@ "data": { "slot": 5, "head": { - "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", + "root": "0x67dafaeeb64b2323fa08e3019ea7c341d99d554e77320667d366a105dff9428f", "slot": 5 }, "target": { - "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", + "root": "0x67dafaeeb64b2323fa08e3019ea7c341d99d554e77320667d366a105dff9428f", "slot": 5 }, "source": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 } } @@ -406,7 +406,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0x6070115be326d9aaf595d26edf621384bbdfc35b4bf5430010c2472ed3064e65", + "hash": "0x6eb660974d0dd40d27c2e4a5eaeb5d72114f7a4fd70572ea1876f4a3ff08ec44", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_back_and_forth_reorg_oscillation[fork_Devnet]", "description": "Multiple reorgs as two forks alternately extend (pathological case).\n\n Scenario\n --------\n Two forks alternate extensions, causing head to oscillate back and forth.\n This is a pathological case that shouldn't happen in healthy networks but\n tests fork choice correctness under extreme conditions.\n\n Oscillation Pattern:\n Slot 2: Fork A leads (1 vs 0) \u2190 head\n Slot 2: Fork B created (1 vs 1) \u2192 tie, A maintains\n Slot 3: Fork B extends (2 vs 1) \u2190 head switches to B (REORG #1)\n Slot 3: Fork A extends (2 vs 2) \u2192 tie, B maintains\n Slot 4: Fork A extends (3 vs 2) \u2190 head switches to A (REORG #2)\n Slot 4: Fork B extends (3 vs 3) \u2192 tie, A maintains\n Slot 5: Fork B extends (4 vs 3) \u2190 head switches to B (REORG #3)\n\n Expected Behavior\n -----------------\n 1. Head oscillates: A \u2192 B \u2192 A \u2192 B\n 2. Each extension triggers reorg to that fork\n 3. All reorgs are 1-2 blocks deep\n 4. Fork choice remains consistent and correct throughout\n\n Reorg Count: 3 reorgs in 4 slots (very high rate)\n\n Why This Matters\n ----------------\n While extremely rare, this scenario can theoretically occur:\n - Two validator groups in different network segments\n - Each group primarily seeing their own fork first\n - Alternating proposer selection between groups\n - High network latency preventing convergence\n\n Properties Tested:\n - Fork choice handles rapid reorg sequences\n - No state corruption despite frequent head changes\n - Tie-breaking remains consistent\n - Weight calculation correct after multiple reorgs\n - System eventually stabilizes to heaviest fork\n\n This stress test verifies robustness under worst-case fork competition,\n ensuring the protocol remains safe even in pathological network conditions.\n In practice, networks self-heal from such scenarios through attestation\n convergence.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json index a0bbab1..e8a4570 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_on_newly_justified_slot[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,39 +31,39 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 }, { - "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "pubkey": "0x3fa3003fd5c7ab54f1a034621281922447676905d22c850e0cca0d09148f7c029f7feb371349fe2e71930249e9bc2d5198c9903f", "index": 4 }, { - "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "pubkey": "0x4f0dfc5ac80e1109e571b06936d7f85f8bd1bb3f3fc51838f54e1d176f1298593d415e24c0a6873775eacd7c2757ca5f1cb8f176", "index": 5 }, { - "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", + "pubkey": "0x26900c5873d6a51ff988450e7b0ff41bc49a852d952e8e5f87880352c1d1293fde4f5d19e7cfe04e7e2c051c56cd3c309cc9ee66", "index": 6 }, { - "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", + "pubkey": "0x9744b954b97d730126325117f225306f5c47ff737770bd5102356347fdaa4d0f1c67dc5f68877f523f47b81438fc780b54267315", "index": 7 }, { - "pubkey": "0x21ade9268c60af157f924f637f4b9c3734b523121efa0b547e21c90d71c14340accca6326736800c2ef1bf61877c986d4ddf835e", + "pubkey": "0xb069f0457ffd7d2bc0c79910cf2aaf0b965fd464b0bea065415e7a2ee5c0235bed07d5663a03f32c3d357207307ca7213d248054", "index": 8 } ] @@ -79,7 +79,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0x54b2feb4bf7af0733124b95734956ea836039d73c6729694f34d931d8153282e", + "stateRoot": "0xced8f337974ea3dc1a918fc1dadda50d2cd629f74c4f5e1632cba3557c4ee50b", "body": { "attestations": { "data": [] @@ -91,7 +91,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", + "headRoot": "0x4f3d8ab9e906b70ecf5be88725455ab6321ed086301178eaeecf82eabb42671c", "headRootLabel": "base" }, "stepType": "block", @@ -99,8 +99,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xf5f1658d4cf27f24ecb6e1eaba8e849f11bf21389c8356602f1fa1ef166bab30", - "stateRoot": "0xc38a9f8c6637d67b91e6afc7ee38c453e31d53489c8d40f82fb19f7f4af49665", + "parentRoot": "0xc3c70f5645f825fcae648cfa73c464ba2c205a725ec12c0b10a47b397b622a4d", + "stateRoot": "0x1d88bf49c2eaf9567f2b7979199598096c8e4f65c78bcb112a9c4fe67d879842", "body": { "attestations": { "data": [] @@ -112,15 +112,15 @@ "data": { "slot": 1, "head": { - "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", + "root": "0x4f3d8ab9e906b70ecf5be88725455ab6321ed086301178eaeecf82eabb42671c", "slot": 1 }, "target": { - "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", + "root": "0x4f3d8ab9e906b70ecf5be88725455ab6321ed086301178eaeecf82eabb42671c", "slot": 1 }, "source": { - "root": "0xf5f1658d4cf27f24ecb6e1eaba8e849f11bf21389c8356602f1fa1ef166bab30", + "root": "0xc3c70f5645f825fcae648cfa73c464ba2c205a725ec12c0b10a47b397b622a4d", "slot": 0 } } @@ -132,7 +132,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", + "headRoot": "0x7dff51bcd788abebf81e6cc2fe115a718fe06220bec2759fca99c4234f87876f", "headRootLabel": "fork_a_1" }, "stepType": "block", @@ -140,8 +140,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", - "stateRoot": "0xcf9d5037d21452fe6face69ecb9399827ff5d7dde448c733530d9f013c30d59e", + "parentRoot": "0x4f3d8ab9e906b70ecf5be88725455ab6321ed086301178eaeecf82eabb42671c", + "stateRoot": "0x9251246b638ebcad9441456f86d4a01401d1669de51b9aa9aff404d09c7c3187", "body": { "attestations": { "data": [] @@ -153,15 +153,15 @@ "data": { "slot": 2, "head": { - "root": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", + "root": "0x7dff51bcd788abebf81e6cc2fe115a718fe06220bec2759fca99c4234f87876f", "slot": 2 }, "target": { - "root": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", + "root": "0x7dff51bcd788abebf81e6cc2fe115a718fe06220bec2759fca99c4234f87876f", "slot": 2 }, "source": { - "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", + "root": "0x4f3d8ab9e906b70ecf5be88725455ab6321ed086301178eaeecf82eabb42671c", "slot": 1 } } @@ -173,7 +173,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", + "headRoot": "0x4231dc11e340635c6242d9d76e68cf87c93fdd41ca3e60452330601904aaa918", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -181,8 +181,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", - "stateRoot": "0x0989b85535d514f2e157feb2721f285adf509a8ce7aaf47287e2307699b29bde", + "parentRoot": "0x7dff51bcd788abebf81e6cc2fe115a718fe06220bec2759fca99c4234f87876f", + "stateRoot": "0xc56b3b35761ebcb87cdc74a322cf32ab4c1cd2287d3d8acfd2932184da09dc58", "body": { "attestations": { "data": [] @@ -194,15 +194,15 @@ "data": { "slot": 3, "head": { - "root": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", + "root": "0x4231dc11e340635c6242d9d76e68cf87c93fdd41ca3e60452330601904aaa918", "slot": 3 }, "target": { - "root": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", + "root": "0x4231dc11e340635c6242d9d76e68cf87c93fdd41ca3e60452330601904aaa918", "slot": 3 }, "source": { - "root": "0x5bbfb6d6e0eb47ebfc882cefa81472e1a295b77ba02871d0cb725bc12f6edf7a", + "root": "0x7dff51bcd788abebf81e6cc2fe115a718fe06220bec2759fca99c4234f87876f", "slot": 2 } } @@ -214,7 +214,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", + "headRoot": "0x64e28a18313356bd15ec37dd80a4129a7612a67f323c978f12e0422c762d2818", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -222,8 +222,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", - "stateRoot": "0xf3529b1b0adc1bb05ff6485e77b93abf1cc16c1c83e9c4932d24590fbedd8dff", + "parentRoot": "0x4231dc11e340635c6242d9d76e68cf87c93fdd41ca3e60452330601904aaa918", + "stateRoot": "0x4721466ce4bcb44da2a6bb3a1f7cf57c1ff4acc18e81d54d27b18a22e1402064", "body": { "attestations": { "data": [] @@ -235,15 +235,15 @@ "data": { "slot": 4, "head": { - "root": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", + "root": "0x64e28a18313356bd15ec37dd80a4129a7612a67f323c978f12e0422c762d2818", "slot": 4 }, "target": { - "root": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", + "root": "0x64e28a18313356bd15ec37dd80a4129a7612a67f323c978f12e0422c762d2818", "slot": 4 }, "source": { - "root": "0xd113b068f02969c279427a90fbc4610eb4854bcec545517e4564abcbdb62d0e3", + "root": "0x4231dc11e340635c6242d9d76e68cf87c93fdd41ca3e60452330601904aaa918", "slot": 3 } } @@ -255,7 +255,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xd74eef9ff6d123c6a90d4d08e12572ec5b4b8dafe4a8f838c41f5a43984671f9", + "headRoot": "0x64e28a18313356bd15ec37dd80a4129a7612a67f323c978f12e0422c762d2818", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -263,8 +263,8 @@ "block": { "slot": 5, "proposerIndex": 5, - "parentRoot": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", - "stateRoot": "0x6fa57208be09487b0cfaaabae48ccba9a335102fa55fd6ae2177d9e1b860bc32", + "parentRoot": "0x4f3d8ab9e906b70ecf5be88725455ab6321ed086301178eaeecf82eabb42671c", + "stateRoot": "0x1e2b8cbe407d036529218f0374e8ae424ff8ae927bdf90386ff4576b8b401ff3", "body": { "attestations": { "data": [] @@ -276,15 +276,15 @@ "data": { "slot": 5, "head": { - "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", + "root": "0x505123b2517ef92df5448175464d050a64b81a497c8dd0cda3864ca9ff52a41c", "slot": 5 }, "target": { - "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", + "root": "0x505123b2517ef92df5448175464d050a64b81a497c8dd0cda3864ca9ff52a41c", "slot": 5 }, "source": { - "root": "0x3add9bf5c7359b5ca66b37a2466eeda4c175735535a4223fe151515c9bd949c9", + "root": "0x4f3d8ab9e906b70ecf5be88725455ab6321ed086301178eaeecf82eabb42671c", "slot": 1 } } @@ -296,10 +296,10 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0x192bc9efe1a001132499fad31956ae165c73d9b37219a7b1bc3b1f82a492412f", + "headRoot": "0x86025bbb95006da908cee2a1d3dc75c691b8a688083cc273edd4cbc65a05e10c", "headRootLabel": "fork_b_2", "latestJustifiedSlot": 5, - "latestJustifiedRoot": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", + "latestJustifiedRoot": "0x505123b2517ef92df5448175464d050a64b81a497c8dd0cda3864ca9ff52a41c", "latestJustifiedRootLabel": "fork_b_1" }, "stepType": "block", @@ -307,8 +307,8 @@ "block": { "slot": 6, "proposerIndex": 6, - "parentRoot": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", - "stateRoot": "0x14805a0ca35798cb8993b564dab4b7e725395224cf6e36fddf507b7f367c540d", + "parentRoot": "0x505123b2517ef92df5448175464d050a64b81a497c8dd0cda3864ca9ff52a41c", + "stateRoot": "0x54295e311c977f019f414c98d59e497674de83ef6d0de2e06800c8260cbfd3c9", "body": { "attestations": { "data": [ @@ -329,15 +329,15 @@ "data": { "slot": 5, "head": { - "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", + "root": "0x505123b2517ef92df5448175464d050a64b81a497c8dd0cda3864ca9ff52a41c", "slot": 5 }, "target": { - "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", + "root": "0x505123b2517ef92df5448175464d050a64b81a497c8dd0cda3864ca9ff52a41c", "slot": 5 }, "source": { - "root": "0xf5f1658d4cf27f24ecb6e1eaba8e849f11bf21389c8356602f1fa1ef166bab30", + "root": "0xc3c70f5645f825fcae648cfa73c464ba2c205a725ec12c0b10a47b397b622a4d", "slot": 0 } } @@ -351,15 +351,15 @@ "data": { "slot": 6, "head": { - "root": "0x192bc9efe1a001132499fad31956ae165c73d9b37219a7b1bc3b1f82a492412f", + "root": "0x86025bbb95006da908cee2a1d3dc75c691b8a688083cc273edd4cbc65a05e10c", "slot": 6 }, "target": { - "root": "0x192bc9efe1a001132499fad31956ae165c73d9b37219a7b1bc3b1f82a492412f", + "root": "0x86025bbb95006da908cee2a1d3dc75c691b8a688083cc273edd4cbc65a05e10c", "slot": 6 }, "source": { - "root": "0xb478313113a443d30348b55aef74f1330ac3d7bf2f8c5ca54802659c7d8b192c", + "root": "0x505123b2517ef92df5448175464d050a64b81a497c8dd0cda3864ca9ff52a41c", "slot": 5 } } @@ -370,7 +370,7 @@ ], "maxSlot": 6, "_info": { - "hash": "0xf9d5c81f2b9be058474fd5b825104a9ac5a0aa72884469b8005a57108a69fbfc", + "hash": "0x66b6b80a4e57f934e8c1a3c738abb1a3af78e66b933d7856eb9c4cc7eac3d160", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_on_newly_justified_slot[fork_Devnet]", "description": "Reorg occurs correctly when forks cross justification boundaries.\n\n Scenario\n --------\n Two forks compete. Fork A is heavier and longer, but Fork B manages to\n become justified. Fork choice must switch to the justified fork regardless\n of weight/length.\n\n - Slot 1: Base\n - Slots 2-4: Fork A extends (becomes head with depth 3)\n - Slot 5: Fork B appears (descending from Base, skipping slots 2-4)\n - Slot 6: Fork B extends. This block contains enough attestations to\n justify Fork B at Slot 5.\n\n Expected Behavior\n -----------------\n 1. Fork A takes the lead initially (Slots 2-4) as the heaviest chain.\n 2. Fork B appears at Slot 5 but is initially lighter.\n 3. At Slot 6, the new block includes attestations that justify Fork B at Slot 5.\n 4. The justified checkpoint updates to Slot 5 (fork_b_1).\n 5. Fork A is immediately discarded because it does not descend from the new\n justified checkpoint (Fork A is on a branch from Slot 1).\n 6. Fork B becomes the canonical head.\n\n Why This Matters\n ----------------\n Justification is a critical safety mechanism:\n - Limits which blocks can be attested to\n - Ensures fork choice respects finality constraints\n\n This test ensures:\n - Reorgs respect justification boundaries\n - Fork choice works correctly across justifiable slots\n - Safety guarantees maintained during reorgs", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json index 48ba1b6..0af1bc4 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_prevention_heavy_fork_resists_light_competition[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,51 +31,51 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 }, { - "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "pubkey": "0x3fa3003fd5c7ab54f1a034621281922447676905d22c850e0cca0d09148f7c029f7feb371349fe2e71930249e9bc2d5198c9903f", "index": 4 }, { - "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "pubkey": "0x4f0dfc5ac80e1109e571b06936d7f85f8bd1bb3f3fc51838f54e1d176f1298593d415e24c0a6873775eacd7c2757ca5f1cb8f176", "index": 5 }, { - "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", + "pubkey": "0x26900c5873d6a51ff988450e7b0ff41bc49a852d952e8e5f87880352c1d1293fde4f5d19e7cfe04e7e2c051c56cd3c309cc9ee66", "index": 6 }, { - "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", + "pubkey": "0x9744b954b97d730126325117f225306f5c47ff737770bd5102356347fdaa4d0f1c67dc5f68877f523f47b81438fc780b54267315", "index": 7 }, { - "pubkey": "0x21ade9268c60af157f924f637f4b9c3734b523121efa0b547e21c90d71c14340accca6326736800c2ef1bf61877c986d4ddf835e", + "pubkey": "0xb069f0457ffd7d2bc0c79910cf2aaf0b965fd464b0bea065415e7a2ee5c0235bed07d5663a03f32c3d357207307ca7213d248054", "index": 8 }, { - "pubkey": "0xf977bd34c9b71d7d009bac5e61ba457349c1f83f1baeaf4884d35029792617306a92a2763ccacf223aa3d90c7d11321dd0634348", + "pubkey": "0x2f75025e7fc4695513dfbb627808e0117273f71b9e717e3aad33f0525677171d7934536b64666859fe9e746cc29cc60e0ef68b06", "index": 9 }, { - "pubkey": "0xde697423036c96403425ed5cbdcee24eb3c5ef41d3de00798d3e683acf715f564b797b46b0de4a46513dfc4a8bc7ee2bd85c264b", + "pubkey": "0x7c72e04be0338e61e9de28324523f028af180e2a89c7e5269207ea6d85f38a61408826424a6cdd1e48c57a4c1ccd5a4542278802", "index": 10 }, { - "pubkey": "0x78b09c1f75c5f5761895d97db3e7a95f3add653b807a782d59bf154191ee6512d9189f1920bf9e55faa86d4aacd01e6c3766174e", + "pubkey": "0xd941c6469c808f41fb62dd535839c247a24f172a10375a5372ae7b3daaf89032d24ad7235345ec609130b3448b19df56f3adbf76", "index": 11 } ] @@ -91,7 +91,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0x5f0cf3503e6923df8318f12208d86b9dacffb90b0d1e766c128e7e46eab7b11e", + "stateRoot": "0xd4518d9d894db86413e27211a36dbd9b2b7fd242d51b59cfb369b788d83e60b8", "body": { "attestations": { "data": [] @@ -103,7 +103,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", + "headRoot": "0x6439252b051ea23b80dec4e5a9efea6fed64725a5751d2abd332fcbe2f61d9d5", "headRootLabel": "base" }, "stepType": "block", @@ -111,8 +111,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xf7070586f3fd7653f33f8843e6cb6aad357fe7301cfbb686eb6e424ec86774dc", - "stateRoot": "0x79a635cee5d0035b7db166a69fa8c5ee548cbe90f130cdae1d690e6aa0240049", + "parentRoot": "0xf04a8e71f2cde5380ebfbcc1f842ba73438f7ae3534d633a18250dc37bed0592", + "stateRoot": "0xa83afb045abd8ff2eec1fa78dc47c05c27e0972c9e9367a6e59fe2065d75bd2d", "body": { "attestations": { "data": [] @@ -124,15 +124,15 @@ "data": { "slot": 1, "head": { - "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", + "root": "0x6439252b051ea23b80dec4e5a9efea6fed64725a5751d2abd332fcbe2f61d9d5", "slot": 1 }, "target": { - "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", + "root": "0x6439252b051ea23b80dec4e5a9efea6fed64725a5751d2abd332fcbe2f61d9d5", "slot": 1 }, "source": { - "root": "0xf7070586f3fd7653f33f8843e6cb6aad357fe7301cfbb686eb6e424ec86774dc", + "root": "0xf04a8e71f2cde5380ebfbcc1f842ba73438f7ae3534d633a18250dc37bed0592", "slot": 0 } } @@ -144,7 +144,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", + "headRoot": "0xc09771be33a5c24d1d671687b4ebcecaf6d751e4034c387c42c7301cadcc0fe5", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -152,8 +152,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", - "stateRoot": "0x73ae00ff75965ce1327db9a66989fc999b466d5b896b496188dd30f74c94426f", + "parentRoot": "0x6439252b051ea23b80dec4e5a9efea6fed64725a5751d2abd332fcbe2f61d9d5", + "stateRoot": "0x5813c710e949e280cef5eb97d2469e0f7b75e0775453c15396fb6abe276daf07", "body": { "attestations": { "data": [] @@ -165,15 +165,15 @@ "data": { "slot": 2, "head": { - "root": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", + "root": "0xc09771be33a5c24d1d671687b4ebcecaf6d751e4034c387c42c7301cadcc0fe5", "slot": 2 }, "target": { - "root": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", + "root": "0xc09771be33a5c24d1d671687b4ebcecaf6d751e4034c387c42c7301cadcc0fe5", "slot": 2 }, "source": { - "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", + "root": "0x6439252b051ea23b80dec4e5a9efea6fed64725a5751d2abd332fcbe2f61d9d5", "slot": 1 } } @@ -185,7 +185,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", + "headRoot": "0x5bb392f191744ca49032bb535a4eb4a1f9c1149aa18524342c2e5c5efd0f87ed", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -193,8 +193,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", - "stateRoot": "0x338fbd52756e4f705764c2e383b8ce9e3e0413533d4632bc4b431424f9ec989b", + "parentRoot": "0xc09771be33a5c24d1d671687b4ebcecaf6d751e4034c387c42c7301cadcc0fe5", + "stateRoot": "0xd31a3ed1a158cc43631ca05fac0230dc553e4bbb4eb971131ef8f718275df1d0", "body": { "attestations": { "data": [] @@ -206,15 +206,15 @@ "data": { "slot": 3, "head": { - "root": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", + "root": "0x5bb392f191744ca49032bb535a4eb4a1f9c1149aa18524342c2e5c5efd0f87ed", "slot": 3 }, "target": { - "root": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", + "root": "0x5bb392f191744ca49032bb535a4eb4a1f9c1149aa18524342c2e5c5efd0f87ed", "slot": 3 }, "source": { - "root": "0x55ce8678ffb31e8c00bb1bca7f72880da74510fded65202bb6fdb73752b9e765", + "root": "0xc09771be33a5c24d1d671687b4ebcecaf6d751e4034c387c42c7301cadcc0fe5", "slot": 2 } } @@ -226,7 +226,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", + "headRoot": "0x54da78b55b11d1e36ed4c143e21d2d5b43f398326f9f6c1f40cd7aebcd8dfea0", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -234,8 +234,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", - "stateRoot": "0x42a4d61c5755f1dd4e10890d4736cf421d1e53265d19051e3c2331cf4fc394e3", + "parentRoot": "0x5bb392f191744ca49032bb535a4eb4a1f9c1149aa18524342c2e5c5efd0f87ed", + "stateRoot": "0x58abea329bc6fa246d7957fc00a732d1a35aea5f04bcabcbf818911ce8078531", "body": { "attestations": { "data": [] @@ -247,15 +247,15 @@ "data": { "slot": 4, "head": { - "root": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", + "root": "0x54da78b55b11d1e36ed4c143e21d2d5b43f398326f9f6c1f40cd7aebcd8dfea0", "slot": 4 }, "target": { - "root": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", + "root": "0x54da78b55b11d1e36ed4c143e21d2d5b43f398326f9f6c1f40cd7aebcd8dfea0", "slot": 4 }, "source": { - "root": "0x138cd9c8c681067093e670cf99fbd3c7d4ba5728f2758c8943d4f9520bf9f059", + "root": "0x5bb392f191744ca49032bb535a4eb4a1f9c1149aa18524342c2e5c5efd0f87ed", "slot": 3 } } @@ -267,7 +267,7 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", + "headRoot": "0xe442211d159fc559f6e7711b663ae1979117c80134b22e4c013bca9424ec6509", "headRootLabel": "fork_a_5" }, "stepType": "block", @@ -275,8 +275,8 @@ "block": { "slot": 5, "proposerIndex": 5, - "parentRoot": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", - "stateRoot": "0x0d778725d02c44533ae9a136df18c0effe21ac44bbed86fdf817a203c3b00744", + "parentRoot": "0x54da78b55b11d1e36ed4c143e21d2d5b43f398326f9f6c1f40cd7aebcd8dfea0", + "stateRoot": "0x07839890e70c16742039bf744fc4bf8724de2b500c8dd69982f77161473e7037", "body": { "attestations": { "data": [] @@ -288,15 +288,15 @@ "data": { "slot": 5, "head": { - "root": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", + "root": "0xe442211d159fc559f6e7711b663ae1979117c80134b22e4c013bca9424ec6509", "slot": 5 }, "target": { - "root": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", + "root": "0xe442211d159fc559f6e7711b663ae1979117c80134b22e4c013bca9424ec6509", "slot": 5 }, "source": { - "root": "0x83ee236b5054ff01b15fe2de907ff6e3f1af451479bdd6bb34b8a257bfd13a8b", + "root": "0x54da78b55b11d1e36ed4c143e21d2d5b43f398326f9f6c1f40cd7aebcd8dfea0", "slot": 4 } } @@ -308,7 +308,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", + "headRoot": "0x79ebedec2358839fddc6cbfaa4e8dda7184e7ef37ddc1778eeef23ffd2053299", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -316,8 +316,8 @@ "block": { "slot": 6, "proposerIndex": 6, - "parentRoot": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", - "stateRoot": "0x53e4772b864743cff0f753902fdc0cf066c7ae0a0b7675d081a701930c2a71a8", + "parentRoot": "0xe442211d159fc559f6e7711b663ae1979117c80134b22e4c013bca9424ec6509", + "stateRoot": "0x69334b80b84c5de7f1354ed91fd2e6407ab3a8978e5edc0b0a2921bc51527d7c", "body": { "attestations": { "data": [] @@ -329,15 +329,15 @@ "data": { "slot": 6, "head": { - "root": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", + "root": "0x79ebedec2358839fddc6cbfaa4e8dda7184e7ef37ddc1778eeef23ffd2053299", "slot": 6 }, "target": { - "root": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", + "root": "0x79ebedec2358839fddc6cbfaa4e8dda7184e7ef37ddc1778eeef23ffd2053299", "slot": 6 }, "source": { - "root": "0x49b1f2f04fdc5ed63dcbd6e1add55269361faf98e2f467a13da5907bb5a94e81", + "root": "0xe442211d159fc559f6e7711b663ae1979117c80134b22e4c013bca9424ec6509", "slot": 5 } } @@ -349,7 +349,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", + "headRoot": "0x79ebedec2358839fddc6cbfaa4e8dda7184e7ef37ddc1778eeef23ffd2053299", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -357,8 +357,8 @@ "block": { "slot": 7, "proposerIndex": 7, - "parentRoot": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", - "stateRoot": "0xcf40e9221cb344f06dd62fe12368941c5c9a853ad95446682a197463bb76fbae", + "parentRoot": "0x6439252b051ea23b80dec4e5a9efea6fed64725a5751d2abd332fcbe2f61d9d5", + "stateRoot": "0x162c1af54a168b7c2b6beba9da5b9e95b260042cddc2b88aaa553ceecacd9f5a", "body": { "attestations": { "data": [] @@ -370,15 +370,15 @@ "data": { "slot": 7, "head": { - "root": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", + "root": "0xa7c0f31b6b1a0e66706261b1ff8a1ea3c40403cd56e2a79567da3a48a7d46279", "slot": 7 }, "target": { - "root": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", + "root": "0xa7c0f31b6b1a0e66706261b1ff8a1ea3c40403cd56e2a79567da3a48a7d46279", "slot": 7 }, "source": { - "root": "0x872c14808d343c2a5bb0045ebaa14dd332883ef5586117e1b9e4a23f1b4a0285", + "root": "0x6439252b051ea23b80dec4e5a9efea6fed64725a5751d2abd332fcbe2f61d9d5", "slot": 1 } } @@ -390,7 +390,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", + "headRoot": "0x79ebedec2358839fddc6cbfaa4e8dda7184e7ef37ddc1778eeef23ffd2053299", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -398,8 +398,8 @@ "block": { "slot": 8, "proposerIndex": 8, - "parentRoot": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", - "stateRoot": "0x31df55327a5165ed894e0e6750494fe93db62b7151b8a36dfe0681f8cf187577", + "parentRoot": "0xa7c0f31b6b1a0e66706261b1ff8a1ea3c40403cd56e2a79567da3a48a7d46279", + "stateRoot": "0x41349a2f87b16a7f9d5de1a71e1ae390f984f534cd2ede816b72f0f99cd45056", "body": { "attestations": { "data": [] @@ -411,15 +411,15 @@ "data": { "slot": 8, "head": { - "root": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", + "root": "0x51b23afb20d24512237263e2ad4344a0666f3121590b99c35b72e32bab50dd61", "slot": 8 }, "target": { - "root": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", + "root": "0x51b23afb20d24512237263e2ad4344a0666f3121590b99c35b72e32bab50dd61", "slot": 8 }, "source": { - "root": "0x62d4c89efd022594e3ba575050856ce31fbed11154f0863b168bad8c52a929a4", + "root": "0xa7c0f31b6b1a0e66706261b1ff8a1ea3c40403cd56e2a79567da3a48a7d46279", "slot": 7 } } @@ -431,7 +431,7 @@ "valid": true, "checks": { "headSlot": 6, - "headRoot": "0xbeba8decc8f6789e9786a826f1a2f8e5ef23fdb5ad6916940717cd6c4405d5b9", + "headRoot": "0x79ebedec2358839fddc6cbfaa4e8dda7184e7ef37ddc1778eeef23ffd2053299", "headRootLabel": "fork_a_6" }, "stepType": "block", @@ -439,8 +439,8 @@ "block": { "slot": 9, "proposerIndex": 9, - "parentRoot": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", - "stateRoot": "0x4e670fcc919834884e77494cf2668a36ce595e0c83101a59c4b9913560f72e39", + "parentRoot": "0x51b23afb20d24512237263e2ad4344a0666f3121590b99c35b72e32bab50dd61", + "stateRoot": "0xea95b634becdc572cda4728fe08f76fd43c30e6e73f3d07912f39652e43246ef", "body": { "attestations": { "data": [] @@ -452,15 +452,15 @@ "data": { "slot": 9, "head": { - "root": "0x1ba5d7d59a3de96f9c89215bd8a886f080876e8d711759f67102ad8988790764", + "root": "0xb8176614d4ad40eecff45182a83f429454df9080b51f84fa29cd25d7b79360e5", "slot": 9 }, "target": { - "root": "0x1ba5d7d59a3de96f9c89215bd8a886f080876e8d711759f67102ad8988790764", + "root": "0xb8176614d4ad40eecff45182a83f429454df9080b51f84fa29cd25d7b79360e5", "slot": 9 }, "source": { - "root": "0x180239a4e443b3d51322b499ddb2e7e0c04b6837ee366299d1eedf1b7f92f0c9", + "root": "0x51b23afb20d24512237263e2ad4344a0666f3121590b99c35b72e32bab50dd61", "slot": 8 } } @@ -471,7 +471,7 @@ ], "maxSlot": 9, "_info": { - "hash": "0x094dbb98a34784ad9fcdeef562f939966d88c5d821f09dff1eb11710756a9199", + "hash": "0x6023413932cda4e5e286bbb77214e99ba600e6e8eb048233ab87648250b9fb56", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_prevention_heavy_fork_resists_light_competition[fork_Devnet]", "description": "Established heavy fork successfully resists light competing fork.\n\n Scenario\n --------\n - Fork A builds substantial lead (5 blocks)\n - Fork B created late, builds 3 blocks\n - Fork A maintains head despite fork B's growth\n\n Chain Evolution:\n Slots 1-5: Fork A builds uncontested (5 blocks)\n Slot 6: Fork B starts from slot 1 (late competitor)\n Slots 6-8: Fork B builds 3 blocks (total 3 vs fork A's 5)\n Result: Fork A remains canonical (reorg prevented)\n\n Expected Behavior\n -----------------\n 1. Fork A establishes 5-block lead\n 2. Fork B starts competing from an earlier slot\n 3. Fork B builds rapidly but can't match fork A's depth\n 4. Head remains on fork A throughout (no reorg)\n\n Why This Matters\n ----------------\n Reorg resistance is crucial for chain stability:\n - Prevents cheap disruption of established chain\n - Requires substantial work to overtake canonical fork\n - Protects against late-arriving competing forks\n - Ensures finality can eventually be reached\n\n Attack Prevention:\n - Attacker can't easily reorg established blocks\n - Must match or exceed weight of canonical chain\n - Time advantage gives canonical chain strong position\n - Network naturally converges on heaviest fork", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json index 6877c19..6c8465a 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_with_slot_gaps[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,43 +31,43 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 }, { - "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "pubkey": "0x3fa3003fd5c7ab54f1a034621281922447676905d22c850e0cca0d09148f7c029f7feb371349fe2e71930249e9bc2d5198c9903f", "index": 4 }, { - "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "pubkey": "0x4f0dfc5ac80e1109e571b06936d7f85f8bd1bb3f3fc51838f54e1d176f1298593d415e24c0a6873775eacd7c2757ca5f1cb8f176", "index": 5 }, { - "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", + "pubkey": "0x26900c5873d6a51ff988450e7b0ff41bc49a852d952e8e5f87880352c1d1293fde4f5d19e7cfe04e7e2c051c56cd3c309cc9ee66", "index": 6 }, { - "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", + "pubkey": "0x9744b954b97d730126325117f225306f5c47ff737770bd5102356347fdaa4d0f1c67dc5f68877f523f47b81438fc780b54267315", "index": 7 }, { - "pubkey": "0x21ade9268c60af157f924f637f4b9c3734b523121efa0b547e21c90d71c14340accca6326736800c2ef1bf61877c986d4ddf835e", + "pubkey": "0xb069f0457ffd7d2bc0c79910cf2aaf0b965fd464b0bea065415e7a2ee5c0235bed07d5663a03f32c3d357207307ca7213d248054", "index": 8 }, { - "pubkey": "0xf977bd34c9b71d7d009bac5e61ba457349c1f83f1baeaf4884d35029792617306a92a2763ccacf223aa3d90c7d11321dd0634348", + "pubkey": "0x2f75025e7fc4695513dfbb627808e0117273f71b9e717e3aad33f0525677171d7934536b64666859fe9e746cc29cc60e0ef68b06", "index": 9 } ] @@ -83,7 +83,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xdd4d50777a2795c87692b1439bbdb6040b261083b5a3fdb227842143320d66a4", + "stateRoot": "0x6461e62d3e84e4944259f334a13ae98001c62090659a92bc1aeb8a24410e1612", "body": { "attestations": { "data": [] @@ -95,7 +95,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", + "headRoot": "0x7a9146c3904e5e0f2faaa6879481bdee712f5cc8fb4d4358fe948b71c7a0d1f5", "headRootLabel": "base" }, "stepType": "block", @@ -103,8 +103,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0x6a2326da9e435e296e0f05d37b574c49de52ba9c004c1d02a83418e092bda242", - "stateRoot": "0x78e57b0b1b061035b18449818a6a79866a9f2b81fe310c272160e39724cec1e1", + "parentRoot": "0xdec18183a93d74460f4fc68bc779b7c6b19b691df8017b0840b43d2a10a5f1bd", + "stateRoot": "0x77228c29372a11c881ee768446db048d7da2a03818285890be8f636a3ac02458", "body": { "attestations": { "data": [] @@ -116,15 +116,15 @@ "data": { "slot": 1, "head": { - "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", + "root": "0x7a9146c3904e5e0f2faaa6879481bdee712f5cc8fb4d4358fe948b71c7a0d1f5", "slot": 1 }, "target": { - "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", + "root": "0x7a9146c3904e5e0f2faaa6879481bdee712f5cc8fb4d4358fe948b71c7a0d1f5", "slot": 1 }, "source": { - "root": "0x6a2326da9e435e296e0f05d37b574c49de52ba9c004c1d02a83418e092bda242", + "root": "0xdec18183a93d74460f4fc68bc779b7c6b19b691df8017b0840b43d2a10a5f1bd", "slot": 0 } } @@ -136,7 +136,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", + "headRoot": "0x4545bfc0e38d150a0542c149f32474b4ddbb00417b44380bb0533214ceade2e9", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -144,8 +144,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", - "stateRoot": "0x498d7fb7b00193a59401b781fd8cc2efb0e9892cff5b4025e92da1bdef6a2788", + "parentRoot": "0x7a9146c3904e5e0f2faaa6879481bdee712f5cc8fb4d4358fe948b71c7a0d1f5", + "stateRoot": "0x61ccb21229f0c49e52ab410258c1b9aff339af8ac535c409cf54cc476c7bbfd3", "body": { "attestations": { "data": [] @@ -157,15 +157,15 @@ "data": { "slot": 3, "head": { - "root": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", + "root": "0x4545bfc0e38d150a0542c149f32474b4ddbb00417b44380bb0533214ceade2e9", "slot": 3 }, "target": { - "root": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", + "root": "0x4545bfc0e38d150a0542c149f32474b4ddbb00417b44380bb0533214ceade2e9", "slot": 3 }, "source": { - "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", + "root": "0x7a9146c3904e5e0f2faaa6879481bdee712f5cc8fb4d4358fe948b71c7a0d1f5", "slot": 1 } } @@ -177,7 +177,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", + "headRoot": "0x4545bfc0e38d150a0542c149f32474b4ddbb00417b44380bb0533214ceade2e9", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -185,8 +185,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", - "stateRoot": "0x22d4e916d14d96579adc6c729933ea817c7be0686d611f88e5f3b2ac7a2b49e7", + "parentRoot": "0x7a9146c3904e5e0f2faaa6879481bdee712f5cc8fb4d4358fe948b71c7a0d1f5", + "stateRoot": "0x85f551514aff9134023f5b0f090e6a228033e01c66785d9fef3e610b2ffd2c3c", "body": { "attestations": { "data": [] @@ -198,15 +198,15 @@ "data": { "slot": 4, "head": { - "root": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", + "root": "0x6317a9f137206d3455e9e14fc7cfc36854b63b5c697d59298ab7bdf1a5aab4d9", "slot": 4 }, "target": { - "root": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", + "root": "0x6317a9f137206d3455e9e14fc7cfc36854b63b5c697d59298ab7bdf1a5aab4d9", "slot": 4 }, "source": { - "root": "0xa714beef7a71b7699d59e902a61e08bffea346ab1cfcf010563bd0bc7782ce96", + "root": "0x7a9146c3904e5e0f2faaa6879481bdee712f5cc8fb4d4358fe948b71c7a0d1f5", "slot": 1 } } @@ -221,8 +221,8 @@ "block": { "slot": 7, "proposerIndex": 7, - "parentRoot": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", - "stateRoot": "0xb082c9d166e932e34355c7b563062f844d8b3dcd595804210600d19cf720d4df", + "parentRoot": "0x4545bfc0e38d150a0542c149f32474b4ddbb00417b44380bb0533214ceade2e9", + "stateRoot": "0xc190fa6a386959fc429655268e6dc9d689e8c445316da8435cf34c660545ec77", "body": { "attestations": { "data": [] @@ -234,15 +234,15 @@ "data": { "slot": 7, "head": { - "root": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", + "root": "0xfaba9d75a6ac5f160f96db463cbd5b18d46ef4090100dc51abdf62fe7548d573", "slot": 7 }, "target": { - "root": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", + "root": "0xfaba9d75a6ac5f160f96db463cbd5b18d46ef4090100dc51abdf62fe7548d573", "slot": 7 }, "source": { - "root": "0x789fb2aea77969bb29a06e639e843fc098388b3426a0b0f4d5224533bb300dd9", + "root": "0x4545bfc0e38d150a0542c149f32474b4ddbb00417b44380bb0533214ceade2e9", "slot": 3 } } @@ -254,7 +254,7 @@ "valid": true, "checks": { "headSlot": 7, - "headRoot": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", + "headRoot": "0xfaba9d75a6ac5f160f96db463cbd5b18d46ef4090100dc51abdf62fe7548d573", "headRootLabel": "fork_a_7" }, "stepType": "tick", @@ -264,7 +264,7 @@ "valid": true, "checks": { "headSlot": 7, - "headRoot": "0xef4459159416712f8b0b54734eba24a452612c3fc0ef6cd405a2a3bc34c1e924", + "headRoot": "0xfaba9d75a6ac5f160f96db463cbd5b18d46ef4090100dc51abdf62fe7548d573", "headRootLabel": "fork_a_7" }, "stepType": "block", @@ -272,8 +272,8 @@ "block": { "slot": 8, "proposerIndex": 8, - "parentRoot": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", - "stateRoot": "0xe5799cce3053ed18101f058b86279ee3506cdc043dfd3d9459e7c8cd275dd70b", + "parentRoot": "0x6317a9f137206d3455e9e14fc7cfc36854b63b5c697d59298ab7bdf1a5aab4d9", + "stateRoot": "0x5d0556071405aa9dccde2bb4d8a694b107eda06846fac5bbd8c1b924be7bacc3", "body": { "attestations": { "data": [] @@ -285,15 +285,15 @@ "data": { "slot": 8, "head": { - "root": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", + "root": "0xcf5ae442a2d3b023c3b3df5bcc6a1e7d0c43c9d4cc3161b52cd0a252540455a6", "slot": 8 }, "target": { - "root": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", + "root": "0xcf5ae442a2d3b023c3b3df5bcc6a1e7d0c43c9d4cc3161b52cd0a252540455a6", "slot": 8 }, "source": { - "root": "0x2232157574ccc4bad3337afe7ede844cd9c4f3a38eb8289601558b983af0e59b", + "root": "0x6317a9f137206d3455e9e14fc7cfc36854b63b5c697d59298ab7bdf1a5aab4d9", "slot": 4 } } @@ -308,8 +308,8 @@ "block": { "slot": 9, "proposerIndex": 9, - "parentRoot": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", - "stateRoot": "0x6606c45e80ca56d447cc01fb1d2883faaa377f15fb3345a14d4ffe1c10065d66", + "parentRoot": "0xcf5ae442a2d3b023c3b3df5bcc6a1e7d0c43c9d4cc3161b52cd0a252540455a6", + "stateRoot": "0x421b3b35d6eab841ff9cdb573be9077c15f7eb01033116cec5d6b55c6f456f0a", "body": { "attestations": { "data": [] @@ -321,15 +321,15 @@ "data": { "slot": 9, "head": { - "root": "0x57a0c119d58646e11a0c8b60efb893537011abb8e026282d95f78bb08b6ee5de", + "root": "0x258883d9cb255dcb8974b2f9fd208fc8ebaba25670e419c5656fa7dd85f9bbcf", "slot": 9 }, "target": { - "root": "0x57a0c119d58646e11a0c8b60efb893537011abb8e026282d95f78bb08b6ee5de", + "root": "0x258883d9cb255dcb8974b2f9fd208fc8ebaba25670e419c5656fa7dd85f9bbcf", "slot": 9 }, "source": { - "root": "0xef608c442bbac06a0f3666b1c264ff6255df50e0f22f0e26715907bdd31c12b1", + "root": "0xcf5ae442a2d3b023c3b3df5bcc6a1e7d0c43c9d4cc3161b52cd0a252540455a6", "slot": 8 } } @@ -341,7 +341,7 @@ "valid": true, "checks": { "headSlot": 9, - "headRoot": "0x57a0c119d58646e11a0c8b60efb893537011abb8e026282d95f78bb08b6ee5de", + "headRoot": "0x258883d9cb255dcb8974b2f9fd208fc8ebaba25670e419c5656fa7dd85f9bbcf", "headRootLabel": "fork_b_9" }, "stepType": "tick", @@ -350,7 +350,7 @@ ], "maxSlot": 9, "_info": { - "hash": "0xbe1f146cff42919afac43d40b9c9d411db32527e3937cfdc31bf763cd8abf651", + "hash": "0x5f6aaad85e060d9668421962be832a54173204493c8b740d6b960502deab04ad", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_with_slot_gaps[fork_Devnet]", "description": "Reorg occurs correctly even with missed slots in the chain.\n\n Scenario\n --------\n - Slot 1: Base\n - Slot 3: Fork A (skipping slot 2)\n - Slot 4: Fork B (competing)\n - Slot 7: Fork A extended (skipping slots 4-6)\n - Slot 8: Fork B extended (skipping slots 5-7)\n - Slot 9: Fork B extended again \u2192 triggers reorg\n\n Missed Slots: 2, 5, 6 (no blocks produced)\n\n Expected Behavior\n -----------------\n 1. Sparse block production doesn't affect fork choice logic\n 2. Weight calculation only considers actual blocks\n 3. Reorg happens based on block count, not slot numbers\n 4. Fork B with 3 blocks beats fork A with 2 blocks\n\n Reorg Details:\n - **Depth**: 2 blocks (fork_a slots 3, 7)\n - **Trigger**: Progressive building despite gaps\n - **Weight**: 3 proposer attestations vs 2\n\n Why This Matters\n ----------------\n Missed slots are extremely common in production:\n - Offline validators (expected ~1% downtime)\n - Network issues preventing timely block propagation\n - Intentional skips during network congestion\n\n Fork choice must remain robust with sparse block production:\n - Gaps don't create bias toward any fork\n - Only actual blocks contribute weight\n - Reorg logic works identically whether slots are consecutive or sparse\n\n This test ensures the algorithm works correctly in realistic network\n conditions where perfect block production is impossible.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json index 2fd5863..ee46e54 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_simple_one_block_reorg[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -71,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "headRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "headRootLabel": "chain_base" }, "stepType": "block", @@ -79,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -92,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -112,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -120,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -133,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -161,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -174,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -194,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_b_3" }, "stepType": "block", @@ -202,8 +202,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -215,15 +215,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -234,7 +234,7 @@ ], "maxSlot": 3, "_info": { - "hash": "0x9e630325338a717aedd549d4636b040fb8cb5fbd63f84024c6c77d6379b1acf0", + "hash": "0x7d680f28b0ece5d50dbda5690d6bfa99bdcc6437b8eab75ecd943038d6207d89", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_simple_one_block_reorg[fork_Devnet]", "description": "Simplest reorg: one-block fork overtakes another via extension.\n\n Scenario\n --------\n - Slot 1: Common ancestor (chain_base)\n - Slot 2: Fork A created, becomes head\n - Slot 2: Fork B created (competing fork at same slot)\n - Slot 3: Fork B extended \u2192 triggers reorg from A to B\n\n Expected Behavior\n -----------------\n 1. After fork_a_2: head = fork_a_2 (first fork created)\n 2. After fork_b_2: head = fork_a_2 (equal weight, head remains unchanged)\n 3. After fork_b_3: head = fork_b_3 (fork B heavier due to extension)\n\n Reorg Details:\n - **Depth**: 1 block (fork_a_2 becomes non-canonical)\n - **Trigger**: Fork extension (proposer attestation)\n - **Weight advantage**: Fork B has 2 proposer attestations vs 1\n\n Why This Matters\n ----------------\n This is the most common reorg scenario in practice:\n - Two blocks proposed at nearly the same time\n - Network temporarily splits (half see A first, half see B first)\n - Next proposer builds on one fork, resolving the split\n - Fork choice converges to the extended fork\n\n Tests the fundamental property: extending a fork makes it heavier.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json index c7e77c3..2164312 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_block_deep_reorg[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,27 +31,27 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 }, { - "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "pubkey": "0x3fa3003fd5c7ab54f1a034621281922447676905d22c850e0cca0d09148f7c029f7feb371349fe2e71930249e9bc2d5198c9903f", "index": 4 }, { - "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "pubkey": "0x4f0dfc5ac80e1109e571b06936d7f85f8bd1bb3f3fc51838f54e1d176f1298593d415e24c0a6873775eacd7c2757ca5f1cb8f176", "index": 5 } ] @@ -67,7 +67,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe483b5237726c89bec17cd6aa4dd397be5b4952d73e7e79d4c4c40b88734227e", + "stateRoot": "0x8f263e7d66474f01517ba675e376c44a5c1520809de0d273a93148f21c07a1ac", "body": { "attestations": { "data": [] @@ -79,7 +79,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "headRoot": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "headRootLabel": "base" }, "stepType": "block", @@ -87,8 +87,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", - "stateRoot": "0x6f64b6379a51d5ea3a51eee1dde0e2457e89942177a60a0705ad295acadf7674", + "parentRoot": "0xa7e0a4699554aefbc06aaf7738da9b04e918748fc41fc567c7a1965fe606748b", + "stateRoot": "0x5cfea33d66f51dc81d89df62ddb734fe8478cd73bf7a4e001a8d8da87b58af51", "body": { "attestations": { "data": [] @@ -100,15 +100,15 @@ "data": { "slot": 1, "head": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 }, "target": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 }, "source": { - "root": "0xecc26d2f68dce759e5e18526fc56e1eea948204a88f4dc518c962d5b0c867845", + "root": "0xa7e0a4699554aefbc06aaf7738da9b04e918748fc41fc567c7a1965fe606748b", "slot": 0 } } @@ -120,7 +120,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "headRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -128,8 +128,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", - "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", + "parentRoot": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", + "stateRoot": "0x910714a078b25da9d19832576fca877a6f250698728096a676bb56cf0cc23e34", "body": { "attestations": { "data": [] @@ -141,15 +141,15 @@ "data": { "slot": 2, "head": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "target": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "source": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 } } @@ -161,7 +161,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "headRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -169,8 +169,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", - "stateRoot": "0x118a870e52cbb414f0bef0d7859d63f3accaa7fc143c1f4b8e78fec8e8a0b85c", + "parentRoot": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", + "stateRoot": "0x910714a078b25da9d19832576fca877a6f250698728096a676bb56cf0cc23e34", "body": { "attestations": { "data": [] @@ -182,15 +182,15 @@ "data": { "slot": 2, "head": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "target": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 }, "source": { - "root": "0x58462b53551a869e66443556544fcc16ece0ad0d5b77014b3eb31ee7b92f698f", + "root": "0x816e46be76da2517089c7cc8339e2738daadd2d4ef6c2d3f82b5b20cb9bbe252", "slot": 1 } } @@ -202,7 +202,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "headRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -210,8 +210,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", - "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", + "parentRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", + "stateRoot": "0x52b8332a0e1e578d164574770d9158c14fd68240f5032157ceeea26efa69c4f5", "body": { "attestations": { "data": [] @@ -223,15 +223,15 @@ "data": { "slot": 3, "head": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "target": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "source": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 } } @@ -243,7 +243,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "headRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -251,8 +251,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", - "stateRoot": "0x0b6b96505432a6a2bd18800b33420dd25ba523efb08569af05961e10b201a964", + "parentRoot": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", + "stateRoot": "0x52b8332a0e1e578d164574770d9158c14fd68240f5032157ceeea26efa69c4f5", "body": { "attestations": { "data": [] @@ -264,15 +264,15 @@ "data": { "slot": 3, "head": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "target": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 }, "source": { - "root": "0xd93560069a806aa0499fd045fc796cd7c59312e4c54cdbbc426c3258d3f2f88a", + "root": "0xc1b73d8ccd16158f95a87d7d42e1427d67d6ca37e203caa64638e2156b073ee0", "slot": 2 } } @@ -284,7 +284,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "headRoot": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -292,8 +292,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", - "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", + "parentRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", + "stateRoot": "0x7c5b159a6fae4548ca6ea2c26670497de1c0b67e8fcab3ced5a1bfb63b7d3339", "body": { "attestations": { "data": [] @@ -305,15 +305,15 @@ "data": { "slot": 4, "head": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "target": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "source": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 } } @@ -325,7 +325,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "headRoot": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "headRootLabel": "fork_a_4" }, "stepType": "block", @@ -333,8 +333,8 @@ "block": { "slot": 4, "proposerIndex": 4, - "parentRoot": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", - "stateRoot": "0x857af29eafb074641c94b270ce0be23cc405450d9afdf04d97570606494b4248", + "parentRoot": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", + "stateRoot": "0x7c5b159a6fae4548ca6ea2c26670497de1c0b67e8fcab3ced5a1bfb63b7d3339", "body": { "attestations": { "data": [] @@ -346,15 +346,15 @@ "data": { "slot": 4, "head": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "target": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 }, "source": { - "root": "0xa8e787359307604c1c7122a39915e90ea1417b444bd62a83cac72c705c174967", + "root": "0x6dc96b404211bea53ddcf4d11ae29a99cdb92ebba31d728bbc19a907c86412b3", "slot": 3 } } @@ -366,7 +366,7 @@ "valid": true, "checks": { "headSlot": 5, - "headRoot": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", + "headRoot": "0x67dafaeeb64b2323fa08e3019ea7c341d99d554e77320667d366a105dff9428f", "headRootLabel": "fork_b_5" }, "stepType": "block", @@ -374,8 +374,8 @@ "block": { "slot": 5, "proposerIndex": 5, - "parentRoot": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", - "stateRoot": "0xa2b05e5ab5824c023fcfce5f1e9276d95c2c034ed07721d059d5ad2c07007d99", + "parentRoot": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", + "stateRoot": "0xd312ce21c03aa35d8db5e0b373b3e4b59af5a474a09a5332aaaf37a59e85a75c", "body": { "attestations": { "data": [] @@ -387,15 +387,15 @@ "data": { "slot": 5, "head": { - "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", + "root": "0x67dafaeeb64b2323fa08e3019ea7c341d99d554e77320667d366a105dff9428f", "slot": 5 }, "target": { - "root": "0xa36b45d5f2872d53dadbf9b61546dbc535eebeb278e9d2fa40dafda815bf224a", + "root": "0x67dafaeeb64b2323fa08e3019ea7c341d99d554e77320667d366a105dff9428f", "slot": 5 }, "source": { - "root": "0x9803bf6c5f2177eb73dba85dc360e30a2b600ea42a99bc41fdb702c9e0f916e0", + "root": "0x4025b184f719578ed0a8e11e7584cd04b7f517f9ff72e97a62d44bc1090a7f1f", "slot": 4 } } @@ -406,7 +406,7 @@ ], "maxSlot": 5, "_info": { - "hash": "0x386b07f20dea1eb1a6a769cf196cfe4880e3b2c03d49bb7df82a8bb32adc4e41", + "hash": "0x6c0c877f128860007e089317a158d18dd3434128978b9c456983066c9e5ec413", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_block_deep_reorg[fork_Devnet]", "description": "Deep three-block reorg from established fork to alternative.\n\n Scenario\n --------\n - Slot 1: Common base\n - Slots 2-4: Fork A builds 3-block lead\n - Slots 2-5: Fork B slowly builds, then surpasses with 4 blocks\n\n Timeline:\n Slot 2: Fork A leads (1 vs 0)\n Slot 3: Fork A leads (2 vs 1)\n Slot 4: Fork A leads (3 vs 2)\n Slot 5: Fork B overtakes (4 vs 3) \u2192 3-block deep reorg\n\n Expected Behavior\n -----------------\n 1. Fork A establishes 3-block canonical chain (slots 2-4)\n 2. Fork B steadily builds parallel chain\n 3. At slot 5, fork B has 4 blocks vs fork A's 3 blocks\n 4. Fork choice switches to fork B\n 5. Three blocks (fork_a slots 2-4) become non-canonical\n\n Reorg Details:\n - **Depth**: 3 blocks (deepest in this test suite)\n - **Trigger**: Alternative fork becomes longer\n\n Why This Matters\n ----------------\n Deep reorgs (3+ blocks) are rare in healthy networks but can happen:\n - Network partitions lasting multiple slots\n - Coordinated validator behavior (intentional or accidental)\n - Major network latency events\n\n Properties verified:\n - Fork choice correctly switches even after multiple canonical blocks\n - Weight calculation works correctly over extended depth\n - No \"stickiness\" bias toward existing head\n - Objective heaviest fork always wins\n\n This tests the protocol's ability to recover from significant disagreement\n about chain history, ensuring safety and liveness even in adversarial scenarios.", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json index b52c7f3..47ce227 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_way_fork_competition[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -71,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "headRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "headRootLabel": "base" }, "stepType": "block", @@ -79,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -92,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -112,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -120,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -133,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -161,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -174,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -194,7 +194,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -202,8 +202,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -215,15 +215,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -235,7 +235,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_c_3" }, "stepType": "block", @@ -243,8 +243,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -256,15 +256,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -276,7 +276,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_c_3" }, "stepType": "block", @@ -284,8 +284,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -297,15 +297,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -317,7 +317,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "headRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "headRootLabel": "fork_b_4" }, "stepType": "block", @@ -325,8 +325,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -338,15 +338,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -357,7 +357,7 @@ ], "maxSlot": 4, "_info": { - "hash": "0x22a626a9971fbedce04e2cb93c8dc0bd769664a284741c1898da84d16a3a3bb6", + "hash": "0xac7885eee02fff5701e651238f6ab4dd7d0a4ce24bae0fc32bb145c3f0c823ea", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_way_fork_competition[fork_Devnet]", "description": "Three competing forks with progressive elimination until one wins.\n\n Scenario\n --------\n Three forks (A, B, C) compete simultaneously. Fork choice progressively\n eliminates weaker forks as stronger ones extend.\n\n Fork Topology:\n base (slot 1)\n / | / | fork_a fork_b fork_c (slot 2)\n | | |\n | | +--- fork_c_3 (slot 3)\n | +--- fork_b_3 (slot 3)\n | +--- fork_b_4 (slot 4) \u2190 Winner\n +--- abandoned\n\n Expected Behavior\n -----------------\n 1. All three forks start at slot 2 (three-way tie)\n 2. Fork C extends to slot 3 \u2192 becomes head\n 3. Fork B extends to slot 3 \u2192 ties with fork C at depth 2\n 4. Fork B extends to slot 4 \u2192 wins with depth 3\n 5. Forks A and C become non-canonical\n\n Reorg Sequence:\n - Initial: fork_a (tie-breaker among three)\n - After fork_c_3: fork_c (depth advantage)\n - After fork_b_3: fork_c (tie, maintains head)\n - After fork_b_4: fork_b (final winner)\n\n Why This Matters\n ----------------\n Multi-fork scenarios can occur during:\n - Network partitions splitting validators 3+ ways\n - Rapid block production creating multiple conflicting proposals\n - Byzantine validators intentionally creating competing forks\n\n Properties verified:\n - Fork choice handles 3+ simultaneous competing forks\n - Head selection remains consistent and deterministic\n - Progressive elimination works correctly\n - Final winner is objectively the heaviest fork", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json index b7bf9f8..3573d0c 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_two_block_reorg_progressive_building[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -71,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "headRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "headRootLabel": "base" }, "stepType": "block", @@ -79,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -92,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -112,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -120,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -133,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,7 +153,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -161,8 +161,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -174,15 +174,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -194,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -202,8 +202,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -215,15 +215,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -235,7 +235,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -243,8 +243,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -256,15 +256,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -276,7 +276,7 @@ "valid": true, "checks": { "headSlot": 4, - "headRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "headRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "headRootLabel": "fork_b_4" }, "stepType": "block", @@ -284,8 +284,8 @@ "block": { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -297,15 +297,15 @@ "data": { "slot": 4, "head": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "target": { - "root": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", + "root": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", "slot": 4 }, "source": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 } } @@ -316,7 +316,7 @@ ], "maxSlot": 4, "_info": { - "hash": "0x761e172b9090081b7b34254bbd54eb3e51338de2f9b80e5f8d28fd5a1b673eb8", + "hash": "0x116a3893f44e2058a7530eb22611f98a86923aa25541cc9f509fbb758ecb1012", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_two_block_reorg_progressive_building[fork_Devnet]", "description": "Two-block reorg via progressive fork building.\n\n Scenario\n --------\n - Slot 1: Common ancestor\n - Slots 2-3: Fork A extends to 2 blocks ahead\n - Slots 2-4: Fork B slowly catches up, then overtakes\n\n Chain State Evolution:\n Slot 1: base\n Slot 2: base \u2190 fork_a_2 (head)\n base \u2190 fork_b_2\n Slot 3: base \u2190 fork_a_2 \u2190 fork_a_3 (head)\n base \u2190 fork_b_2\n Slot 4: base \u2190 fork_a_2 \u2190 fork_a_3 (was head)\n base \u2190 fork_b_2 \u2190 fork_b_3 (tie at depth 2)\n Slot 5: base \u2190 fork_a_2 \u2190 fork_a_3 (abandoned)\n base \u2190 fork_b_2 \u2190 fork_b_3 \u2190 fork_b_4 (head - REORG!)\n\n Expected Behavior\n -----------------\n 1. Fork A leads for slots 2-3 (2 blocks ahead)\n 2. Fork B catches up at slot 4 (both at depth 2)\n 3. Fork B overtakes at slot 5 (3 blocks vs 2)\n 4. Two-block reorg: fork_a_2 and fork_a_3 become non-canonical\n\n Reorg Details:\n - **Depth**: 2 blocks\n - **Trigger**: Progressive building on alternative fork\n - **Weight advantage**: Fork B has 3 proposer attestations vs 2\n\n Why This Matters\n ----------------\n Demonstrates that an initially leading fork can be overtaken if:\n - Proposers switch to building on the alternative fork\n - The alternative fork accumulates more blocks over time\n - Network temporarily favored one fork but consensus shifted", diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json index bc8289c..bc7d1b1 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/fc/test_lexicographic_tiebreaker.py::test_equal_weight_forks_use_lexicographic_tiebreaker[fork_Devnet][fork_devnet-fork_choice_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "anchorState": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "body": { "attestations": { "data": [] @@ -71,7 +71,7 @@ "valid": true, "checks": { "headSlot": 1, - "headRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "headRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "headRootLabel": "base" }, "stepType": "block", @@ -79,8 +79,8 @@ "block": { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -92,15 +92,15 @@ "data": { "slot": 1, "head": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "target": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 }, "source": { - "root": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "root": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "slot": 0 } } @@ -112,7 +112,7 @@ "valid": true, "checks": { "headSlot": 2, - "headRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "headRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "headRootLabel": "fork_a_2" }, "stepType": "block", @@ -120,8 +120,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -133,15 +133,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -153,7 +153,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -161,8 +161,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -174,15 +174,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -194,7 +194,7 @@ "valid": true, "checks": { "headSlot": 3, - "headRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "headRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "headRootLabel": "fork_a_3" }, "stepType": "block", @@ -202,8 +202,8 @@ "block": { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -215,15 +215,15 @@ "data": { "slot": 2, "head": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "target": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 }, "source": { - "root": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", + "root": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", "slot": 1 } } @@ -245,8 +245,8 @@ "block": { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -258,15 +258,15 @@ "data": { "slot": 3, "head": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "target": { - "root": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", + "root": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", "slot": 3 }, "source": { - "root": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", + "root": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", "slot": 2 } } @@ -277,7 +277,7 @@ ], "maxSlot": 3, "_info": { - "hash": "0x268fc7be347b5cf7f041b3136edcd3ca1348ac4ff4606923c8e939c75d63c08c", + "hash": "0x8aba9aa539e3d29ad352b5387f2706296b6884eef70ce5ddf4cc89bbe403223c", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_lexicographic_tiebreaker.py::test_equal_weight_forks_use_lexicographic_tiebreaker[fork_Devnet]", "description": "Fork choice selects lexicographically highest branch when fork weights tie.\n\n Scenario\n --------\n - Slot 1: Build common ancestor\n - Slots 2-3: Build fork A to depth 2 (slots 2 & 3)\n - Slots 2-3: Build fork B to depth 2 (slots 2 & 3)\n\n Both forks have identical structure:\n - Same depth (2 blocks each)\n - Same attestation weight (2 proposer attestations each)\n - Same parent (common ancestor at slot 1)\n\n Expected Behavior\n -----------------\n The competing forks have identical attestation weight. The head is chosen\n via lexicographic ordering of the block roots. The framework automatically\n verifies that:\n 1. Both forks are at the same slot (equal depth)\n 2. The head is the lexicographically highest root among them", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json index 92e3cb7..316b1f8 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_at_large_slot_number[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,8 +59,8 @@ { "slot": 100, "proposerIndex": 0, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x6f9b7a10879510ab1e1c4246d655cd08a7a1db491580a20c47eb019c08e54f50", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0x455cc7ae8833343ec8b2df20b4c64b22d0173eeb22129ff69bf23ac2b98fe781", "body": { "attestations": { "data": [] @@ -72,7 +72,7 @@ "slot": 100 }, "_info": { - "hash": "0x792bfdf069ad579de1f37e34db90c37bfb66082c866c7afa390626fa7b9a166f", + "hash": "0x6bf12284f3bcd59681e7d859a95364939a7503d532bf5111bdc7f05bbb8792f5", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_at_large_slot_number[fork_Devnet]", "description": "Test block processing at high slot numbers.\n\n Scenario\n --------\n Jump directly from genesis to slot 100, simulating:\n - Network bootstrap after long downtime\n - Test environment with artificial time jump\n - Integer overflow boundary testing\n\n Expected Behavior\n -----------------\n 1. Process 99 empty slots: 1\u21922\u2192...\u219299\u2192100\n 2. Block at slot 100 processes correctly\n 3. No integer overflow or wraparound\n 4. State remains consistent", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json index 16d3aab..efab07d 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_extends_deep_chain[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -70,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -81,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -92,8 +92,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -103,8 +103,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -114,8 +114,8 @@ { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", - "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", + "parentRoot": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", + "stateRoot": "0x3ccb488b064dae8643040852e10acc9276aa087d85bb82387eb29101245d1d35", "body": { "attestations": { "data": [] @@ -125,8 +125,8 @@ { "slot": 7, "proposerIndex": 3, - "parentRoot": "0xf4243749bd2d853df2b0956fdadf2ffd80c7503484271c29e612c6e9924155db", - "stateRoot": "0x708526b6a7f5619cc96d42ccad93c5c246e3911261d98669a5ffa9b3edeaecf7", + "parentRoot": "0x73181ed32f3aa2a920928042fa3067fc83bffb51e04c228275cf78d74d0582de", + "stateRoot": "0x924c49d73043292db24525a72614bc79453028c75a9fb23b27a0e4e557331560", "body": { "attestations": { "data": [] @@ -136,8 +136,8 @@ { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x8a62bad3ff9ec014fb6adbf8519e493678bbaf90fac55b47fe53d2a840d70833", - "stateRoot": "0xde084595bc25fb4c1089fbb9b498a3834ad27cd7848b1b5d38a55d15d6d86893", + "parentRoot": "0x61915407f528a08440e478209d32aa118ff4d94cf3f14cb165668a9b57a0ce5f", + "stateRoot": "0xcf16ef20643079955b1ee11f6e73ad0694d6c0a2f97d6732e43dc342d6277bbb", "body": { "attestations": { "data": [] @@ -147,8 +147,8 @@ { "slot": 9, "proposerIndex": 1, - "parentRoot": "0xd942b458e5c8b796edc932d23df9dda8939eb7b96f8c94d2a17bf368b807348d", - "stateRoot": "0xc8f2880bc8e3d1ba655c75f34ae5a20555503ce8296cbc3a2a266fbcdb7b078b", + "parentRoot": "0x84ab94ea44818ec9523d6b9b2a345469d7e8b59f5c7b2aa499228f1fdfa58acc", + "stateRoot": "0x4a76b547a30d61443a8e045ef40912658582ba64fb8c6d483c207f8f7a55f063", "body": { "attestations": { "data": [] @@ -158,8 +158,8 @@ { "slot": 10, "proposerIndex": 2, - "parentRoot": "0x3957ca3fb02f85e64ee684e2c2536bc87fb2e975a4dcdfa3081de9cc39ddcf6a", - "stateRoot": "0x054571db4eb9706f6f29a97bffaa00765b2a75b77ad5812e8619066e942d3192", + "parentRoot": "0x0229002eb2c6851eea6087f0fff2ee6c7514f4c180298897f889fe4cba086dbe", + "stateRoot": "0xd424ce4fe3a44ae13b83ebefe1efaacf805e00dd4ca5839ce2d0788de4936864", "body": { "attestations": { "data": [] @@ -169,8 +169,8 @@ { "slot": 11, "proposerIndex": 3, - "parentRoot": "0x5ecbcf9082feefb76c1dd6f37d8159c3169c2ecd2bbb14bc497dff4a3d5641df", - "stateRoot": "0x4a359bc7767356dd13c761cc98bd9f0a62013636c0395e9babd5d37794c9c728", + "parentRoot": "0x153df1278b07bd582253a28b0c7ef81c8766784f3eb6422387c1b12d46c9e339", + "stateRoot": "0x5bd597860d3f6c089f438b5ae3aebcb1858bb0dd74c09ea84c6f68f4dc1f4880", "body": { "attestations": { "data": [] @@ -180,8 +180,8 @@ { "slot": 12, "proposerIndex": 0, - "parentRoot": "0x2e9715cf83119270ac92321616d630c8e787ee27f23a91aaf8207295d122b7b5", - "stateRoot": "0x584b4ae5faaeb8a0cc6b190b4f570d773fc3f40426c0eb5a1f1730b199633b0a", + "parentRoot": "0xafe577c68f6d988a1e94f77615f8e7af3bfb6d6a261b1de5cd45379ff73af943", + "stateRoot": "0x2cd6d0d82adf90b137c6f4ba447cd7be99e22ee890568024ba9b51a41cef58e2", "body": { "attestations": { "data": [] @@ -191,8 +191,8 @@ { "slot": 13, "proposerIndex": 1, - "parentRoot": "0x535e1e1271317cd5a83c6ec99c30d972927fee6952250a12de1572bc6d27050c", - "stateRoot": "0x4f3ed1bd4a625037ebbfdcf9b32a384ca5233b432df7e421f1d09af26d2bd841", + "parentRoot": "0xbe2c70a92d3748061fbf2101aa48fc262b25a03670a6c5b746f32f3c0dc7434f", + "stateRoot": "0x7009e11b299137957d24d0bc79ab0221c9a078fc577b02ddf072d23be92111c1", "body": { "attestations": { "data": [] @@ -202,8 +202,8 @@ { "slot": 14, "proposerIndex": 2, - "parentRoot": "0x92ac402c6efb6b43179d5a662dcd7389ccf61617c7968d3d5af611160497362e", - "stateRoot": "0x638ae62be9308adae58e1239e365a27d053727985b6323652cb66c8a84926e88", + "parentRoot": "0x5f2f83ffb2f0281eb2d6aff2677a185f373af6c06a783f738a67315c64ebf092", + "stateRoot": "0x9142529b461b68fa191e2d37ff121905c21d05bcebd1c89bcee0119fdf019467", "body": { "attestations": { "data": [] @@ -213,8 +213,8 @@ { "slot": 15, "proposerIndex": 3, - "parentRoot": "0xfa599ebfafdd658feca9f4bb9095a4cd19e92ba23b2572a5e42f2a188ad7ab19", - "stateRoot": "0x0792f723312433e078a11523609eb9b90a961d0ce1346717f86dfb56a09712ca", + "parentRoot": "0x275e535cbf60d7169d60649017c17ff747be42a3b3c2590f96bb4534af2e3182", + "stateRoot": "0x2badbb1cf8283532ca7d12385a471b9a36e0f9c283ebabb871d7e32cde9e97b4", "body": { "attestations": { "data": [] @@ -224,8 +224,8 @@ { "slot": 16, "proposerIndex": 0, - "parentRoot": "0x8f3032c7b8c2e1283df2c580dbd315b17ba369b382f5c4c52f107b65d66d4b41", - "stateRoot": "0x76f71fb8340d28f209436d7f5a46d5ab47f6200ce78e98028869edcc17306c36", + "parentRoot": "0xee614ab3e9a7c43312665cfc7bfdabb729b4f3eb7dec215ac33440162a345c25", + "stateRoot": "0x3f26419b913372f2471f36726a3ad506221bf90a19b81e966f37f2ed24ce74eb", "body": { "attestations": { "data": [] @@ -235,8 +235,8 @@ { "slot": 17, "proposerIndex": 1, - "parentRoot": "0xa3d9765056efab51625457713ee53811602353a08a2fa7d609e1ae053b82bc81", - "stateRoot": "0x73fb22f47f7645fe9c7484b48946bdcc9feefb77c89119cac4ebfbd897efdf05", + "parentRoot": "0x8827968d431bd6f7e36997f2f41a95bf04aa4e0c4f42c6801f2636fe86890ff6", + "stateRoot": "0x1f744250089fe0cf97c1d495f3c023bd1393f03808b7446bc9e12b336fc86552", "body": { "attestations": { "data": [] @@ -246,8 +246,8 @@ { "slot": 18, "proposerIndex": 2, - "parentRoot": "0x32f71624e9cc375bd80c01c52b543f11400add4817497445cca0387c5e93a2da", - "stateRoot": "0x304172e1836eede1f7a460cd0466152ea27449ced669a4e42133579f86d32640", + "parentRoot": "0x860dc08df8b00d119b7afa35e1ceac2688a4459575d9cb1d4b3ffe854d07bb06", + "stateRoot": "0x33551af50a0c022f962af04a12cd1cd6dcdccdd4fcc804cdec3380e1d3e5ef02", "body": { "attestations": { "data": [] @@ -257,8 +257,8 @@ { "slot": 19, "proposerIndex": 3, - "parentRoot": "0xb815fd73d480ac5fcbd4ba6c185469c1974823c7df6bf23dfbedc9ee9b29834d", - "stateRoot": "0xb0bfc653ee2bcbe154796b6f0449d89bb63b090444b361e9a86eaccc74898327", + "parentRoot": "0x890f60672917e6ce8905deb825006a15dbe14bd8477cb73dc62122b2b4e0113d", + "stateRoot": "0xeb08b169c563c8497b5ac6d062c43d6a5d2b150ccd03db8e448bba2cde3fac51", "body": { "attestations": { "data": [] @@ -268,8 +268,8 @@ { "slot": 20, "proposerIndex": 0, - "parentRoot": "0x9339a784accf839ae09330f6a9cfe88154b4f7fbd6f825d2930e58000cef5d65", - "stateRoot": "0x086dd2f62898dc60b1c7a964f30ee820c2fcb855c06aab1db1df6a7bce28690b", + "parentRoot": "0xecb8a0fc314d5d9b7e7041e7e6624f10b42762c48f290e0424c853b7f14ad845", + "stateRoot": "0x35b8931929c034d3d613f57c1ade031f7fdef003251c7a7043cad885e8f1622a", "body": { "attestations": { "data": [] @@ -281,7 +281,7 @@ "slot": 20 }, "_info": { - "hash": "0xec6542115b0f17f95f10889d3979d360390d3ad83ace61f5e9beb6aa80b3d447", + "hash": "0x44fac8af1f01ab97fcd1e350bb4906fa7d44bf57121fbf6e7100dd7ed4a37ea9", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_extends_deep_chain[fork_Devnet]", "description": "Test that blocks can extend already-deep chains.\n\n Scenario\n --------\n Build a 20-block chain to simulate a mature blockchain state,\n then verify new blocks can still extend it correctly.\n\n Expected Behavior\n -----------------\n 1. All 20 blocks process successfully\n 2. Parent linkage maintained throughout\n 3. State advances to slot 20\n 4. Historical roots accumulate correctly\n 5. No degradation in processing", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json index 60b535c..67787ed 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_parent_root[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -70,7 +70,7 @@ ], "expectException": "AssertionError", "_info": { - "hash": "0xa69cd326fd14a060fb9e770eff2407fa1d418245b188c6ef976df87acee884a5", + "hash": "0xb59ea28148b116cc01bfa07ab28357c75e6b741b4558e402ff35b692310fafdf", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_parent_root[fork_Devnet]", "description": "Test that blocks with wrong parent root are rejected.\n\n Scenario\n --------\n Attempt to process a block where parent root doesn't match\n hash_tree_root(state.latest block header).\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Block parent root mismatch\"\n\n Why This Matters\n ----------------\n Maintains chain integrity:\n - Blocks must reference correct parent\n - Prevents chain history forgery\n - Ensures linear chain continuity\n - Critical for fork resolution\n\n Without this check, attackers could create invalid chain branches.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json index 06ff9a6..105481b 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_proposer[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ { "slot": 1, "proposerIndex": 3, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "body": { "attestations": { @@ -70,7 +70,7 @@ ], "expectException": "AssertionError", "_info": { - "hash": "0x4072c329bcc00c6b8ab9c2e5f6fa55618f598635eb7002a6164ae122d551e11c", + "hash": "0xb45434f975584f6530884cf438a3021c2f609ccb0e32dfa468912ac537384d07", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_proposer[fork_Devnet]", "description": "Test that blocks from wrong proposer are rejected.\n\n Scenario\n --------\n Attempt to process a block where proposer index doesn't match\n the expected proposer for that slot.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Incorrect block proposer\"\n\n Why This Matters\n ----------------\n Prevents unauthorized block production:\n - Only designated proposer can produce blocks\n - Prevents validator impersonation\n - Maintains protocol security\n - Essential for consensus integrity\n\n Without this check, any validator could produce blocks for any slot.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json index 1c3db2d..66a7bde 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_state_root[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "stateRoot": "0xbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaadbaad", "body": { "attestations": { @@ -70,7 +70,7 @@ ], "expectException": "AssertionError", "_info": { - "hash": "0x5e785147cab337f4e01e3a8280a3582ba87d607bb348823bc769e76e818c1980", + "hash": "0xea6849fca98ce01ecd1544f9e0101d6930aaf8e3fe874cd632f2f70a39a9b8d7", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_state_root[fork_Devnet]", "description": "Test that blocks with wrong state root commitment are rejected.\n\n Scenario\n --------\n Create a block with state root that doesn't match the actual\n post-state hash.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Invalid block state root\"\n\n Why This Matters\n ----------------\n Cryptographic state commitment is fundamental:\n - Proves correct state execution\n - Prevents state manipulation\n\n This is a critical validation - without it, proposers could claim any arbitrary state.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json index 4c81ae5..bab1496 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_wrong_slot[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -11,7 +11,7 @@ "slot": 0, "proposerIndex": 0, "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0xe964061dcf66d2772fea43f77cdbf563b6642b7d13130b42890618b16e67c1f2", + "stateRoot": "0x1548aed67cff3c3c02bf84906531c1558ecd4492175212f2ee7e8a6754e99d25", "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" }, "latestJustified": { @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,7 +59,7 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "body": { "attestations": { @@ -71,7 +71,7 @@ "expectException": "AssertionError", "expectExceptionMessage": "Block slot mismatch", "_info": { - "hash": "0x3e9a6c55ea851ac9514f3db8a433d571552638e884a3cbc64b10c2966f14fa24", + "hash": "0x4d79e8bd893bd5e8d42ff3247ba12692b98a5c2cfb2a2c155c9959058f992683", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_wrong_slot[fork_Devnet]", "description": "Test that blocks with mismatched slot are rejected.\n\n Scenario\n --------\n Attempt to process a block at slot 1, but the block claims to be\n at slot 2.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Block slot mismatch\"\n\n Why This Matters\n ----------------\n Ensures temporal consistency:\n - Blocks can't lie about their slot\n - Prevents time manipulation attacks\n - Maintains protocol timing integrity\n - Essential for slot-based consensus", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json index ebbd53e..98a8fcd 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_blocks_with_gaps[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -70,8 +70,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0x789b378042d8d7c40826e8a4ac9d56b475824914fd6064e9d89a700502cd8d6e", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xd1c45b5cb30899c19437a0c61ffdc56371f334f79efd3b2c156667ee2d2a6ba1", "body": { "attestations": { "data": [] @@ -81,8 +81,8 @@ { "slot": 8, "proposerIndex": 0, - "parentRoot": "0x0cd63b486d1dd15e47fed3c75d4b607fff03b0c384d2eeb0359d8a602ff9d8d4", - "stateRoot": "0x03c27bd408b531f462b5266bd73bc74681eaab69b9f6b209519faa6c0d0ecc09", + "parentRoot": "0xacb9f659fa745d5ecbe7292567d273432c07baa9c26fcd44643235cbaf0e1ea2", + "stateRoot": "0x77ff09d69da93bddd0bc2c18edce4c6f092d38a24c5e9aec68fdd22a7a436860", "body": { "attestations": { "data": [] @@ -97,7 +97,7 @@ "historicalBlockHashesCount": 8 }, "_info": { - "hash": "0x6a5933287f43f90459dbc020c904e6a5b309adb8dd56f65dd5f422ce818f03f9", + "hash": "0x2bfb5aa59d5cd8287998214476bdf6a689465949bb78e5fd3cba871d3e256588", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_blocks_with_gaps[fork_Devnet]", "description": "Test blocks separated by empty slots.\n\n Scenario\n --------\n Build chain with gaps:\n - Slot 1: Block\n - Slots 2-3: Empty\n - Slot 4: Block\n - Slots 5-7: Empty\n - Slot 8: Block\n\n Expected Behavior\n -----------------\n 1. Blocks process at specified slots\n 2. Empty slots handled automatically\n 3. Parent linkage spans gaps correctly\n 4. State advances to slot 8\n\n Why This Matters\n ----------------\n Missed proposals are common:\n - Validators offline\n - Network partitions\n - Missed attestations\n\n This validates resilience to gaps.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json index 310e089..085125a 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -70,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -81,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -92,8 +92,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -103,8 +103,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -114,8 +114,8 @@ { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x58e9edf92e29d841a4eba44782a3d4d1e0c993a2a8227848b244f1c1e4bc52a9", - "stateRoot": "0x11f9d81d1858d36589ccd75c3c2bce69eab77aae77d6977f30116022a23cfc18", + "parentRoot": "0xfd5fc440e10000b159efd9bbcd71ddc36d1510050e9c2c28a6580adfec5aa5aa", + "stateRoot": "0x3ccb488b064dae8643040852e10acc9276aa087d85bb82387eb29101245d1d35", "body": { "attestations": { "data": [] @@ -129,7 +129,7 @@ "historicalBlockHashesCount": 6 }, "_info": { - "hash": "0x56feeb1d2a8ff7a3eec36c167d794aaea4f996643bce794940dfc44db01b6ff9", + "hash": "0x23e331f7f0993188ff0a6dbaeb775f1d132bb748751e8069175659710d5c7860", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks[fork_Devnet]", "description": "Test processing blocks with empty body (no attestations).\n\n Scenario\n --------\n Build chain of blocks with empty body:\n - Slot 1: Block, Empty body\n - Slot 2: Block, Empty body\n - Slot 3: Block, Empty body\n - Slot 4: Block, Empty body\n - Slot 5: Block, Empty body\n - Slot 6: Block, Empty body\n\n Expected Behavior\n -----------------\n 1. Blocks process as expected\n 2. State advances to slot 6", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json index a756efb..517d3d9 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks_with_missed_slots[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -70,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -81,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -92,8 +92,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0x6c26076f86536098f57f9a530b9a58873498c5bd7173cccb220b9e0a4219871d", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25fa364e59eb9246b102c71bd3ae6cfdbb9caec04fad2a7c80ad45808a51a7d6", "body": { "attestations": { "data": [] @@ -103,8 +103,8 @@ { "slot": 6, "proposerIndex": 2, - "parentRoot": "0x041eee0ddc77a228175d6ff90c00a8ede62ede3431a90f54e452d4b7a17a5ee2", - "stateRoot": "0x768566c1db94161383b795b29fc4e5e470af9e476fe2c0e88d289f4b52185a81", + "parentRoot": "0x15febb9a2ef68f54b5ec3144cde9e0beff92b8adc02d1e4a0b58ce750f610468", + "stateRoot": "0xc1c718fc7db36c0c9c6620a45f9a775a1e9b7aa4a5888b1f92bafde11565a33e", "body": { "attestations": { "data": [] @@ -118,7 +118,7 @@ "historicalBlockHashesCount": 6 }, "_info": { - "hash": "0xb858e8ad3f7dd0ca7b079bc5b9f2472626f277572b055d938f8d0d683f2da30b", + "hash": "0x1c7177604eaa86287e779a62115cb4a31c9c40b2a422cb8eff8d9ffd7a113819", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks_with_missed_slots[fork_Devnet]", "description": "Test processing blocks with empty body (no attestations) combined with missed slots.\n\n Scenario\n --------\n Build chain of blocks with empty body + missed slot:\n - Slot 1: Block\n - Slot 2: Block, Empty body\n - Slot 3: BLock, Empty body\n - Slot 4: Missed\n - Slot 5: Block, Empty body\n - Slot 6: Block\n\n Expected Behavior\n -----------------\n 1. Blocks process at specified slots\n 2. Empty slots handled automatically\n 3. State advances to slot 6", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json index 5171fd3..6e53e77 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_linear_chain_multiple_blocks[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -70,8 +70,8 @@ { "slot": 2, "proposerIndex": 2, - "parentRoot": "0x4d16ec7e8dacf5f60d30ca84cd0a789a022a91ff3dee22975240d617c4236b0d", - "stateRoot": "0xb7e6a86335a19a6d737b0b3f9f60c18f2eb175206459e84b9d3b3807702acfde", + "parentRoot": "0xb447f7732c577d6e5c7aa205293c8100971c0e68ada9acf0b8e29341737079c7", + "stateRoot": "0xb3699f2b96e988fb2b56a9b763428d595965bf567abc02ab1df3bbb1fc81f3e6", "body": { "attestations": { "data": [] @@ -81,8 +81,8 @@ { "slot": 3, "proposerIndex": 3, - "parentRoot": "0x64cce9455f0122be56727da4c100f9c842d93bd55c116d36648f312071968d23", - "stateRoot": "0x106ac834106f37ca1cb2152976c703f396030741fa17e4522acda3f586287128", + "parentRoot": "0xe901f17c0f7efc8cbea86867c49520c19c31e4dce70a37af1c66e750dfae1034", + "stateRoot": "0x6808d136ffb18190b851b4a395542d03308f10624a7d597e9f069edb07fe982f", "body": { "attestations": { "data": [] @@ -92,8 +92,8 @@ { "slot": 4, "proposerIndex": 0, - "parentRoot": "0x068babbea4aa0202a94dd95e4e3f65b32e95cefe143df7ef0c76cf83858597d4", - "stateRoot": "0xf26c35d1dcc06eb67df91399e6d09294b417308ebb016a9e983cd6e41a47b211", + "parentRoot": "0xb3f2bcb1d0d55ad30003261e9dbe26e364d7dd6de94ee624a6007f01018498a2", + "stateRoot": "0x25d25b22efaea06ef5d831bb38b08cab848c7b6459f632c45df1192b2c0e2dbd", "body": { "attestations": { "data": [] @@ -103,8 +103,8 @@ { "slot": 5, "proposerIndex": 1, - "parentRoot": "0xed871b60007be196769e0a763169d230f898d665087bcadb5815da47477dcce8", - "stateRoot": "0xde9a2c56034dfd16f10d63014fdef7e7a443d49a7b3c0df79ab064fbed9db2b6", + "parentRoot": "0x23ea141268b5113ebf4f44313e14e159a22a6b521f48b4797d06df76f1275617", + "stateRoot": "0xf65e20721a8f2f222cb077a579c1e369101291adfca53b35e67cf9a9efb450e2", "body": { "attestations": { "data": [] @@ -116,7 +116,7 @@ "slot": 5 }, "_info": { - "hash": "0xb3347c2521c58f977e4b0810fa04762675de07d33d3359b36cb1de57943f815c", + "hash": "0xd4848a8416440d7de78e8b422c84061940a6efb0efafa4379f30a1f7a3a2227e", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_linear_chain_multiple_blocks[fork_Devnet]", "description": "Test building a linear chain of multiple blocks.\n\n Scenario\n --------\n Build a 5-block linear chain:\n genesis \u2192 block1 \u2192 block2 \u2192 block3 \u2192 block4 \u2192 block5\n\n Expected Behavior\n -----------------\n 1. Each block processes in sequence\n 2. Parent linkage maintained throughout\n 3. State advances monotonically\n 4. Historical roots accumulate\n 5. Final state at slot 5", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json index a5c42c0..49fcb74 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_block_processing.py::test_process_first_block_after_genesis[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -59,8 +59,8 @@ { "slot": 1, "proposerIndex": 1, - "parentRoot": "0xee3755ec636c6cabd4fbec9791efd12fb38fbad6989e09258a286dcdfa84ca0f", - "stateRoot": "0x976e5f2bf6a9b4c51516e650d3a7c7f0066580fa3d1b185808a5bec483292a2f", + "parentRoot": "0xe8626f2aa0da826506456b41d8bc0abdd09875df71a21c08fae337f17152b23c", + "stateRoot": "0xf880311e5121aee43e1c9a6d696884ab455fd889eab3763c553b96540e4c6a48", "body": { "attestations": { "data": [] @@ -75,7 +75,7 @@ "historicalBlockHashesCount": 1 }, "_info": { - "hash": "0xd1bdc1835e365b2a98c5c31169bc4db5357cc21d1d81a16e935eb2028e12749b", + "hash": "0x0f23fef828567c3abdc5d8b39d55cece7c27c057dbf85294042a78cd535b3e16", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_process_first_block_after_genesis[fork_Devnet]", "description": "Test processing the first block after genesis.\n\n Scenario\n --------\n Process a single block at slot 1 immediately after genesis.\n\n Expected Behavior\n -----------------\n 1. State advances from slot 0 to slot 1\n 2. Block header is validated and processed\n 3. Latest block header updated to new block\n 4. Historical roots updated with genesis\n 5. Post-state at slot 1\n\n This is the foundation for all subsequent blocks.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json index 72d1126..bb8e1ff 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_time[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 1234567890 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -83,7 +83,7 @@ } }, "_info": { - "hash": "0x1676cd789fda952b19f0592c94b020bf7a43523aab0951e65d17c13ec5241f12", + "hash": "0x7df1a4806954c180abb7472b98c2407e81e060276100b3b67d36c8d11807a1b7", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_time[fork_Devnet]", "description": "Test genesis state with custom genesis time.\n\n Scenario\n --------\n Generate a genesis state with:\n - genesis_time = 1234567890\n - Default 4 validators\n\n Expected Behavior\n -----------------\n Genesis state should respect the custom genesis time while\n maintaining all other genesis properties.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json index f4d9dd6..c91eee4 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_validator_set[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,35 +31,35 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 }, { - "pubkey": "0xd84fbd1ad59fc5331284b7230d73cf63aee8e65322eae85ce6b06d408087fb192ad07648e42ee229cda8ed266a86905252eed57b", + "pubkey": "0x3fa3003fd5c7ab54f1a034621281922447676905d22c850e0cca0d09148f7c029f7feb371349fe2e71930249e9bc2d5198c9903f", "index": 4 }, { - "pubkey": "0x0fb6092458097055b7b5695aee9a3306a70db27dee357637555504587cc3b70979a27e476d06f656db47a868aca9b01e1efb1978", + "pubkey": "0x4f0dfc5ac80e1109e571b06936d7f85f8bd1bb3f3fc51838f54e1d176f1298593d415e24c0a6873775eacd7c2757ca5f1cb8f176", "index": 5 }, { - "pubkey": "0xb5cd3b4395f76558823a0f16eeab034d31e5024576d8f5529e5e3e666aa5c764d80c035ed260fa6be73ca72517e05135f264f819", + "pubkey": "0x26900c5873d6a51ff988450e7b0ff41bc49a852d952e8e5f87880352c1d1293fde4f5d19e7cfe04e7e2c051c56cd3c309cc9ee66", "index": 6 }, { - "pubkey": "0x522b0815c617a3545d5b53361a454251369d923ffc4b174a5712ce01a20fd60572579c3e03bf093b1015ef0fe6063e22ddef214d", + "pubkey": "0x9744b954b97d730126325117f225306f5c47ff737770bd5102356347fdaa4d0f1c67dc5f68877f523f47b81438fc780b54267315", "index": 7 } ] @@ -99,7 +99,7 @@ } }, "_info": { - "hash": "0xeb9c32040335d481228d18e590c01445d4e545d953d55cd14121013bd438430d", + "hash": "0xce395688e507905dee0a4e0cdd12f1cc4be2ef607c9dbfe80ef2dd5c0d0dff47", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_validator_set[fork_Devnet]", "description": "Test genesis state with custom validator set.\n\n Scenario\n --------\n Generate a genesis state with:\n - 8 validators instead of default 4\n - Custom validator pubkeys\n\n Expected Behavior\n -----------------\n Genesis state should contain exactly 8 validators while\n maintaining all other genesis properties.", diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json index 3662f9b..a2e5a03 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json @@ -1,7 +1,7 @@ { "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_default_configuration[fork_Devnet][fork_devnet-state_transition_test]": { "network": "Devnet", - "leanEnv": "test", + "leanEnv": "prod", "pre": { "config": { "genesisTime": 0 @@ -31,19 +31,19 @@ "validators": { "data": [ { - "pubkey": "0x8fa00001b5b22a073d21cb60c9465b379f80e74fee9baf1d137eb651ed162900a6f3ec32da74ff09265ea62fc9a5055b2e9b7c7b", + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", "index": 0 }, { - "pubkey": "0xbc56037ab0485c00441e3205d908e55a016d5450a335144f50ccb034cb253c05a1cbc047b5695a5672697d584656e96a4b94f173", + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", "index": 1 }, { - "pubkey": "0x79422d64ecdc3b0bf7c6cc31bbfb9f4eab7950107fe23c6f4f9c94465c81f80d85a1931f1003c50531147f350357805f22fdd70b", + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", "index": 2 }, { - "pubkey": "0x575bb17b0f673d76689c9527a21fac68eb5ebd571703a245deb2a8384baea4747be00d5b469ff30f830e234f74453f6e2c20cb26", + "pubkey": "0xc7adde03d418565797f15a5b060ed4073d34856dd5941f77077e1d509952a674df084f0c5b57331e8af15364d8a29914f4a90178", "index": 3 } ] @@ -83,7 +83,7 @@ } }, "_info": { - "hash": "0xdaef5b4f74195028f564a2f9d827ed277fff97c5127c208440b06640008b522e", + "hash": "0xc27d979939715b6ec3c3c5774f3be86ed351b64c73849d39aecd8985ca878e64", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_default_configuration[fork_Devnet]", "description": "Test genesis state with default configuration.\n\n Scenario\n --------\n Generate a genesis state with default parameters:\n - genesis_time = 0\n - 4 validators with zero pubkeys", diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json index cc95a6e..fb3b40c 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json @@ -107,7 +107,7 @@ "hash": "0x8d97e6b6a601e10856ae70720ffad8cd392dab8169fe158054edac0bc0f0e49a", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet]", - "description": "Test that invalid signatures are properly rejected during verification.\n\nScenario\n--------\n- Single block at slot 1\n- Proposer attestation has an invalid signature\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation is rejected\n\nWhy This Matters\n----------------\nThis test verifies the negative case:\n- Signature verification actually validates cryptographic correctness\n not just structural correctness.\n- Invalid signatures are caught, not silently accepted", + "description": "Test that invalid signatures are properly rejected during verification.\n\n Scenario\n --------\n - Single block at slot 1\n - Proposer attestation has an invalid signature\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation is rejected\n\n Why This Matters\n ----------------\n This test verifies the negative case:\n - Signature verification actually validates cryptographic correctness\n not just structural correctness.\n - Invalid signatures are caught, not silently accepted", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json index 427c51c..5d5d3a6 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json @@ -120,7 +120,7 @@ ] }, "proofData": { - "data": "0x08000000d50004002f00010000000000be78fe416bd6fe14000000001bf7ff64dafeff25f2feff0d2e7c29022906d430ed6eb2416f07bf12d01e6e0954c83b6b95023b142293481ab8261b39f7b4657d95e2be088c3a105659d0f6565d936e2bfa9cb175ed360500609f5e633149104dc4454126d7288e34a0a26b2d992c3e5709a02c2b6e52471d7cd254412621a979ae63da120321252d4311c1033c48f368d48b1006864f12774144a52b247d71570106a30211ed6139dfd24d080cbfea4d02658629e4410655f71c3949310ab81f213d420567a4fb4362363a561ba4714bf4a9431a3cea72129d42b205449f353dc610a22a4c590f79caee0373d0769362fda2c640f0aa0e12f3c6e72fce5c6a271f073f5b66f6a73a171d913290e0ed6943dcc60c05be0a474e8eb14e0fb4c842472348050b5d862be39c4801239ce812f0ccbb08c92fe3791a655a6d349e674f4ae6273ab31f340f45e39f07e4fb8b4f2313705249a5b94d6e634a1b1b76dc710205f96c09fe7d00e655f84960bbf71eeb64ec6c0e3a985813d8343496e50042dd4a7d7501683a09e9a79a4df7c5ba3a8bc7a9573ab99f4e6e604902ab0ce82bd88af17ed23fe0788897cc263730201d6d0c473bc2a53279e2b84e01f5e84935e4e1b36dfb6e3675ab147b40858dfd25a8b766250224fe7c5abc7373e78ed160e483f938dc13dc2d7baeb26e18190136078cca2026acba3e16283542d27e337cb39c854b37baf71df51ba5497b7de67035b83709e1acc83066e68d00cb9725085799e763f892221bcc17d425de0f2e1ac0686f4541946a7768555769f097d73842047428fc18561b31acbd74dcf1761467ff01459b771873c328a52f64c95255deb1a63dd0367c4475f0001a027121008abed41470cf545a00cb19164108d959e797994531932f57b2f0c13ec68215766f31265f29bf5e3fd41e885aac47f333b043b50e6c84b74ad8c08e0cad3b346a14dd5e44407a592047b6240d028eff6749140e279ec7396ee5b4c65bdcfc283471c17a3586f1e93314270949096f2275ea7b1e50d394d0199bba2762c449702eaa848021c9df98711662bd4cfd1e563e3d677504e7f7355f824f182adc4c775fb433ab4cc6444e184c57780c9850282a01d7e07725e25f6f9fce523101adf70b1807e6733874232df0e4e15c2adaf50a06812e09e3a2476a13f5685d7715f255cbdac7641549633895ed6918a8335f03cc7e98023533155dff8e8e0cd904836bc597bd72630b8c78f7d27304ded23c119a954b43d0fa8d100af141045b8fd0670e3c546064bae1094ef45b3c0252bd26f9c55567127d3b1fe37f2a675e872e1aaab43705ab2ad4447753d175d8e8786bbefbe04dcb0c276afcb49e6f27458556d66f4b4fc5341d5fc253dc43a0d23f1a854d0c557e0c67320dd1760ba5bd797bdf8b6427d20ef163930f80059cd9c44e0645375b46d16b28233a75468fe4a40aff113402922c3809f76b296cb4c2237ef32b6b63e87333752cefbd5f9200b531da96af19ac166957d7d9e411ba47686ce574490240407e41e9c1ef41b4f2331183aab6784461c364cd61d85f7abd5259a1c0441887017d7a7ea05764a4b568683c38d3301eb1556e0d1e2078117868276d46f24c7c48a35629058e5e46baca74b90f3a7b747b3521fdb94d1d233cd85f44117700eab1fa2ffdc4e10dc649c61aabdf9839a2ae2c5d75830f56fb93057594f9203cfd1f324fb4042134875a761de7d50e14125a187d31506206e364701023cf9d11d3cda0062ea01a0336c457567195eb7d27fec76431916a53128d8e0b97e91814d3c9235d2b9be969fdc03e038b37476aad8a5534ddf9611c84bf983b7fca8073b816397c4150c25253fe8959a841b4416c1ade5393fd305e66211664a6dbc52ec3889623b4217d43e80ce45e66510e6c5c03811afe116d0c3ad00108c325105c81503f371a25b945cb4a9307e501a844094ba655658da2354e926c713f7a562185a59650ff362a2e62efac4ef07f7e18b135fe24e21fdd0a8159f8177205ac5791290a5fbcae424437afd36f92ba805639cff564ada48c32ed52ac0ad9e99528ee498f5a78bbfc49f9581e7293def375dba17022949ef24c97112e4d9ab8de25b0548c69e4107c7d1da16e4dc573d604c3f6bd53a5ab8b5f8d229c0fab0b5552d61e95280f1a73098c3bfb1539b7ee17731236421a611950371dc715753c7a00bff81777c44a967618e0e602b080862d5f390308a31f5f4a0cebbe319220b85c21b5f21f0cd5ab242d5f0f7ad5676259535ac46f664fbe557618fc595e95e50b3951f37ee15dc356b55d5771925c236321d4d66ddff7377936691b39c1bb097918faa309e932d24af26d75525c59e72d0729085c93534579c6d0f270663e3937212dfb41256ec523e9d9b87138dbf6333c867d37f0a2a9697e6e511e34545051c5f8031561a61b580d576a2c678cce148c5b2034f25f1f01a16bc00741e6e57c12765b13fa94215ecf0ae806a8a7a4651285653a3573d33f701aa6606d5b3862e33e1a09f2d27709c5ffcd019c329049b58d966b078c5815f7ea1d15fa5dab16b15d5e135b83230d5d4c8f7ef0e803375a14a85fc8113b2019162c432bb6804115bd3663c0e3931d3eba60397bf8fd5f3d01fb5621dc9c5549aeac0c6de42c75a40cd02f7ee1545eed7ab51670c4a55e757d2f4eac6e2e38bf38ba300106c8531fd56459a1fd5a73b06ba90676db2625bddaf6705b413a16c1dedb3e46de9349056b9d77f99c0832ca20fd4392ae9d0ec651cf6a722d104ab16a2705a5b769447f70997ecee1a03fb58da918a453ba44939cc307b41c1d0bc1390e46ec84d72a7a89a1070f193665304d8f27c1a22a2bde048a49fbc33302fa582f3c813f2d576da19e5606b7540444a96c19904dc41512cc1e1ec3840c21f974806af94eb20aa86c2d7c7ea9ea7db4af09746e3eb43c710861426dc0eb6cd481b74074b35d0410fa06044534ad45a9d73a467dc8fd6701dc5177ab1a4d46e392cf7d6bcaa45c1a813f1d710fef09ad47f74487a2914ac96ac16e77d4226a26521c1f857904651eb1257ad1752435c2f3ac13c64670121083f87c70e2d91b5b7b621dc048cc52ff644a5c904ddf169d303e415921d94c091be37a7632920f1c018b0da572a17d02cee5172f076f688fe4246afa55ff69f6775817faf9ee374be69b5b8deed80a1b8e192f52f7b90e8ee20057f686ea08b65b102021abce2a61e1f3504731420cc2426b5d74fffa6137c1f84c9090b478c0ae1370f426aa11a241dc466b1c85550f68db423d2edd6907d91d37df7f150cc47ede2447f8a14860c1452143235e5228c7a12a9c602c588180267dc9a94f4d7eee221c5e47703c24b8377ed9a4261fcec19b057ff7587c17bd9818e8d4b975eb9bad05c8046e24f5bcfb6353abc554a0156b4efd42d32d826d982973f2f551cb18891d647fc61ceb569d2302f7b47426ea5a3180540f0e9c48120d0292704b4b4f113b0ceb911a2e4cfa7a959d1a775057e50d230d8a7db7f4774edbcb4f204b41015d7026b02e9e67cb327879d709c3eeb126ce912422fc0ac21095db061ba9c45d2608672d77dc80546881194d2c8a533046ca9f2862642d9d471280ed67c22f3f7a95e44810691301308fa4d712aa84c42ef9246367f1e9af74821c100795f8ea00cd147f41387f0c618d74111e1904cc0030b6935b8956814a0bfe240d265e5b0473b02043ceafd6413656a0265a531d74baaf5d7328b0ce4e23dd86574e449d16489fec19f1308a0741eb905570ccc40df6bc895eafbb7f4481a3103bfe4d7629a76ff8449e42f10522e10666c3316f30dbf0140818df8111dfcf7216d76fec604f92bf0fc38d7d3cf6bff1798d5aae4610a5b77aa30fb573f052ba6f66c79b731747655524a2795f8fb55f0ef83bed26c013c57e9d829f52c969d5088c253c1a758c1b185d134d021387fd5193a30d3f1a9cea1b34d012515fce78281c911e42c39c870e04f9170afc42634c784033271a16bb56cb94eb5d5c25ef21f113036e26bd732019ce1273a91fc3694b880e2922a3f1156696487e5c9298234071ad18ff1d967b9b040a7a33d85e00d1a3a21d7d8d9322c2b6bf3cbc29e048dd3bbd0f9c99042661d030591beb6e27fd172d4a8cabc4519ec3102ae01af8164e159728501a8c0ae4be134ddc8c667e0c1aff743a1b8c54cb106e674f80ae280de27a794adbc8539d017a7491c0ea4e6730a34640c3d1546a261414a865087e5f21d70564ad5c0e861b7c7521915a28f4a1376788370f5bac760954571069731f6e41746d06f25818d1e5334b052a09d1a8603a64a8db5804c56e424c75160439f9b47df32227375d1c796e7bcf487d2b0d4f68242d872563df3b3d88e0451338598d563f56f474ec15662abca1cb39e1547646d9914d2275a2745f241aef0cb88b327e0f56d15d50de97094a09557655701a1a03c0115afea8a6174c288b5db76f816ecd1c7b718df36447d88b963134e9a6479b319a6d1b63d744d9ea6f475ed06731facfd63da21958147dac0c729265a64e318c3b49d86bd233d442fa744f1f4c5927dfae01c412ed770d552b31f00fb72b177a89729ad34b74c841ee57d3132171ab9ae9351f6a71548c91200636e3df37756ac826e564de7da04d4f75d4adbd144488bd7d18f7a6431f2544611cd12c304e836722e8a12d18028ace4c58c72c1b0d706f2f2970d40146a3d574d2b2f355af149871ccc67701db5038323dccf94f7776db5cf7ead91e0dd11c7600ab7f49d1ca0347a41f20058a2733556e936129dca75a319f694f1dc1dd983636d40a7449411b3ab19013568faa2a0cffd9cc5db6d62a4aaca91d30bd528021f4e7707b5a8a7b0d1c9667264ce71a1ece16ce54fcb57e609d15a319da48a74d0fffd857b4033a1502548c0ee1c1324ab6c91e0366e9e81999cae419dc0d2065f708db0119b62a4ce1fd18315ea8f83de9ee5078801d1d44ed868d69d2f8e47edd7ae73453b9c46e9d3bfe618fe05762d7aa5328a94fe5159b72567098f29a3c9113805b922fb51e3faecc204881a37b30c0cf2c09199564e7856d669c9a907e415b3572848ba17cc35d6b62c99ae52c4463be4d9eebe143f3ff5f6f61aaa60582f0792d2c800f47a2d9a661251de54d3ae2634dcf7eed27cfd97d5167cad77842d6bd5ff5dd9e6df71f9b692b7e7f0e2df5151066bb946c9a9ba01811a2c1388480d063e2646c5cb8cb9071a2db644ea6663727cd374e23e36e0e690ca0631088600b10b0d162563e72575a19a2b529c422d66913219a4c728de8056b26f55b28e6274bb394834f8f866e5e63b88c0dc188065b58183d1446dcb7510801621a250d456662986a4aea51be1072a14231d9eb004a92467450c0f8192df5e38378e6581c000da7c738d379ac2c78b25600be57be4447cf9f54d4131951ff93343293840430b252a04dc44d855c27dd3b58a5cd732107489e2eaf797e48f4b881029aeafe2a2b7b9108676a647efbab826dba805e786cb11d0c2eaacc22a5bd694c59d88a4761cbd76682d5074d96878959e15648115e933c6867518e61ec60c92136e6d253edeb534ff6ad0f2f0509753f8cc54f4fa872cd43407bf345e24aab2b7a7bb01d4fdc0906ea0df6120f21dd3e119a74693e69ce51e127d2462db7556aa662e07da36fc7339cb82a30d7a866769db774617710ce286cf2a2300a585d6ee058543732aeed2e83598150016ddd6cb1b2446ee338f6556593550b4a0dc90abad3756cc0d9d563dc362a397690497e6178c4730b77b3163611963efdb9722041cb5679381dab5cc7ad6d596ed0d22fef036c6081be503450f81443e5ce34235904880822106a2399bc7b4ee4895e0f830c5d7a923579452f7a7009faa3734ca6138d1722f4514e0f946949e7ad9378ec9c96739ffc7850f7a58c36357c676d3f94713f1f4ebc6480de9c609d02e61714252755d9afb220205ef96426bbe96789863701defbd119a372ca28beaefd7a8386b670c28544414f3c575f93c2d0734da3472660f4b82fd3014679b2de80072a8c0d490a890774d7d07c38b886976d5becb4286a89523a0cb2444b37c3561c52eff659b6cc0b52f17cd90f0a82d95d0856b60e40d00f2df8a32a403ad5a35ea633ba1aa07f483759bb0f09fd2b4204bcc0e0245f48c858c833597d33886e1b2ca0594f7b1cf812c9534918425ea75baccacb71e79aac3cb8365948f35fa44d2b75c130a3048e5507d8ff2ab4dad74522e986561490944aa04b3f0dcfa2925a93e1fc3fef4160044356013234fccd60e761820abeda771a8ee16d6700640251ee12af61f2242537f3299e30ac087348c79393631b347268c9316554c1daed7a72310b3f3450e460e07e9f02ae4fa70beda9f02066d8ba328ef8283ed670fe18de6dde0c215f8325e22e4e0316398156981f4b63a5085e0b8044a62c6bf0705f162568526296c87100a70665f559073f562d30037e9b7e5f8046ee55775c7a613fef366e0bda8669350c2711606f7219240d7930ad9fe6550f872a3162db384eed518e55fb6d58118e492c21cb8c2a6e806f0423d70430760b0fe37b124bfb6ca51729005d66cb1402c70b10c45f921a4733243624c86c6614ca575759568b1381942963ff2293687fdd9c155503c06f0972f17a02b4c240fe582d571fe4a75e83fa8e5719850c1e44e8687d5bf1c105cb76a824c14ce1713718f00f70560c18dd444154f84ffc66150f0367a6d47a08d1056c194f19854e6118dc77a9961d3a9cec343eef616e66ce451303a749662a2f2a020f293a6d59b002e70a3e0e9003a7794a2424cbe4710538b97b56445014504ea103ca522029b8ab254e6cd14931d936b31a6deb382e773bd70415bd3a6d10225513950ead6528f70507349f2d0342c2e70a8ec4b0237b74d8163b32344cfc8deb60cec6cc666e0e2670c03ac87dc1873c058deb64749de05813a0bba328575f8d3934ed8540cf07d70f8b636d6dca66492617376e541ef0680697aa216e3cf6b32042f2d71332bc130d20e34746e1cb13472f72d037e186ff6e874c0c1544e9dc5e934e9f366868851909c3680ba627486960857e13c4d21f6ff94a6216626e4d294403d51a79250f6f23b6f72973df1574bce17f2d2e09a95c99562a245e96374144354e7846854703b4004c2e3df14e2db944cf63f761cf20ece65426f21b8f22051b12131ae070492a7f6101523fce1b886c18494aafc53c22cd3e5d56107f1f0c51b7520778c762d231830d0d81907345ca461a497c670c5e90f7703891343c57176128074b8c0481c1314a50cb9412ab31c930ed72712bb742217d6ace4d0a7cb6f3123854103cad5c643e7f7b3412ee36506d531a1800f0a9b068f4b0471e70136d30cfc4ce3341308d0a7a81d94c807d8553a382bb6763e2681037c48b46722af44dea0aae48238f3243b4f3f21b183f53194de7430829a9a95a58beeb36bbce995c82fe7f2ecff66e27d9503a0973e6b60e47b7567e8117da42d10f1512c36a4c02ed57e5112e967d0c26844a7274adae1356fb9c2baa75a90d915d5d112adb68016d7941531e44ac5db1a1b611234e7f771fe1ef742c1db64a8eeec617913c4132ebca95608326d6367d4baf61281309155c7c15736a30ef78614a806c2e7b9e3a5b93e70f0444965fa7947e555b26d22eb587d45e8ffb92445dcf8800544be310dad4156355afbb4550b1e055fe2e1b1e83caca60dd6c641e05548e569366e30e144edc2a1ba3901989cebe501e99ab4ce746603e00ce957b167dfa7408a37b5272ef6c14ee7ead762ee8320c34f42520437e2c2cffe9ee6e7fcb16613f4a111dd871493ca711d1796d44552b4a54ca2fbe8f5453725a296eb402dd2ae617fc135e9e58749af4ae17927586297babad56a69f6d6f5b325b477f97a11ca36ee52c55f3ad6bf947fb018a26d531a8f1051cf61a811e6818a628b13961359417152025320430f8e735395464051799b66f2597b1473a3253b226fb3f594380a5247a416e0a178b414c67ac46a937f685d800713a26413b43b100c3e6356152a6fa1a0ae215628e4aba7060282374fd7e90451fb0982a3c81355199460f1de45972547355f519db2d71761c56e65bb854370f3129ad2a4efc8402fdd5533f1a04703add6c474de4ea0b6aeb322126e5fd0615ff8cb5449bce560ddb1a922ed2e49371431d013b692c6c54bc85b1730f21a51f9cbe687e4dcaf200e0980258907e563a95611e22279a57422000b27cdfc5b57c38133c306528320d4aa0213632607a17fb088335960e10207a16aa4d52a3213d56a5a43fc367b263335ff648e6ac145d9e9058732cf0315df7c6317652c5cb1d8eaf4c58a4019514d8bae80df26db55cf1ba9a10ca963c2e81da512f1c21f640d2e67c1e276d4d2de8cc1478523a0a1ef00fc26c24c35d636cd3205e639e47314e30ae677ca116022cb5030e7c02c870259fbf4a7694623e053e0a5b26a873081ba06b56e562696232786e195985623aac4fc41f98f0a11f7c915a05a3d7a9278da55329073a8d22d6ef660f1f6b96408a577c45d133905aaa2a504966a3c43e5d7c35756c38f1658498b038f104dc37a619cc700bc3884527e8187c8aa42d2bd092523128f12975625dcb5e2a5394271bab3a65405bce5c71b6d121ed71fb4ee0cb5f309f49e30eaf78251ee4c0124fab1e5e7d99bc5065c8a8b70a1af709183536de35d0d31f540153c070a6170e5839dfae011686654a4a9697384c80770357c16d106ee745057bb92a289270871a11d0a115204e5f1d6dec395f76b42130cf07907505ccba4039d0766806e7512e6815da61ca4e16157b12bf5b409c695d7ef226108ba573469d0e8079c2ea9d27833e215058612114e0d22d1784f5823a8a13387e958dad0bfe25c956235344251ff2181bd6e81b1e623687426d2fbd3813d9ca7c2e25d21300cd9e329baae2363e58471de6f1b51bd44c1c210554652a5c51d171ee705824c564a46d5b4c2c7051e2775a53eda27a99850971df9cd11da44f4e0994afbe5693905d53e05f1437f8cc8d0304f9ce696a292261652090347712fd14ac16710960e6c02c10325d7e301af330af415c28349606495b4eeb6ae610b13d40e22727e8fd06437dafbe2803d990530381f92c794236337f843e33be99201540a6a842f558b545692d287dd6750a2ec63dd10284efe16c3f23ee507b2d4569bb76be0edb43f959e12c003f3ad15136ce50ba1b55ad0217606bda2d95c949558b09f90bf3bd775daf169166191262034ff0b53f611faf7e7f2d7d1e11cc32104a22f47c9157895727df3f28e1dfc160111f0243f2efe1660dc4067cde04b63f4b87f471ad479a3d61e18c7ac337745b2e7f5e2d869599706016905e6109dc068f94e70f78b770055dccb11e163f172d32e2d02b422db07320793163d464fd38b7ee065a4e722e52ed3c294e68ccd54862a34746a79d331ad32a8a797fd559603a8f865ae48b3a5a5225f33c588fd342db686f481bae3b158a94120cc739dd1d542c762044f1270a35e3f44dabe7cc5d7275a8452882cb6cd27ccd271da1770cf36ff61271357e5bfe2aee2866bb186733c503340084ac42f68cc31a113d3e78cf0811365f8b783db7c520735bb3905fcdd0ae017596d401a75da053907fc301539ae66c14454554440354386058f04ae3508c2dbd36ea6b2a6faa32a447277c931fc121b74e0e188392473b2ec4821dfd55bc11db5f5161358eee6c6f179a21837f8f30e834964c4b69206cc3f31f4eca5be15e917837743e4e332ec8f68060a117ed14d969f7424079d5556addf158c8e23315a4f53f446e949b4535d7853d36e2fe48d37c471dd7e17d5fe79b2208bd1f895deb9ace21a2edf176674dbe659914446eab7ff73029d2931cea6aee08cd768d2e81c9da3524a2387449674c7ec8b5057ceba0691752e1152525de98334175a649cf96592cf6c16765621daf16551bc313e142b067a39fd25e78d6e263247ba106594d7f045dd26b0f32d1d94dd13e915197422f76e3817613daf5b94e5c44011df303486f103338385f886a3a49a9650c087a876553aeb5394e773727dadb0850f52895497975f965dc8e825b995d374f43da152ab1f4ca5f2a0c0c6fefe1844e5a146570b5553164a3cea55e4971864841244771e166c02b1a5d593ede7cdf495272325402e95504d263477261e3e2597690911a91b91c77ec4d8e2f6014e14474421915ad49f01b1c59805d1343516d6a9166261cf95d2e760f2b68315757500ef8e2373509c269abe029136978dd0cc579a5666a8b5159c0c7fc5b7bb86d749b10df487d69e55d3b59f52edbe8900d59fd761b52ec986569f676384b9af66d37589d3f2768d667c090253527ccea713a272555b0654421ea69c02c6e58a17e88457e78821e5018c1e9d724c537d971bea2aa215b9a2b1bf17d940e56d522260e7eff1cc4d9941858db3569550a5f17ea89d2709f53a16bc64c8c049f4b8c4347c45e1e86af99162149a941192a6609fff69b02e9fdff57b2993d10e4e508742b04e2001307913bf6fc8068f97fd247160d0d3e8c245362d937ef1a87fa9c18106a3b04a545457b2c40e83f6b571a302fb069466267373c485458387a84ac7861d8e401a7f2806113594c37a2a1d9430d48c81b005a5d23a00b617b20a64c4199a5060aa5e3ec63db185b42b71a035a1adbf80849362237714d376797a483355e6fc614390c9f188b4ea45e2c336c2cf844326439525847a53692784922fb10642582591d10005cfefac820e9aa2a52caa7ad39fabfea73781e4b724b23e57bf486cb32de8f1d24d6f8d97203d4554b76b29007ed66c80d4e3caf540f7b62414cf26a5f019d6d6b6c4b7a399b168f6443622a2716785c1d3aa6cd4b934fdc4f69c22906ac629c26f4987d7e5b89693c7491a2020d71fa5c1b9ab0138a578459c801ee345d71155b7404f7797459cb2cd061bf2f1ce224321c2cb235fe2e4d0dcc9dd14614cdb94e05e1c36a883d826997d592796377e64b8a97464befc6721282db8f6eeec5a02e9da50e20028c7801ff9d00616e60cb0cc2a2871c442675624dfa89056ca50625f8d28e564d11d030ec283426c2be9c35ee537228efcb9646baa25a07440e812b62eea619596c830b6cb3001b1864171ff4a28f2bb773cc53c8316f6dfb421d799de20523b2f51a171e3ca7713d1d082039464153cec7d57e19c0806522503d416cac9e4d946025553704ed072c602a4fb9807400fefbed7c7caf564253d082196100733bacd98963c34ed6655a5f6528787da759414c2161a60eb018faa6094e0e9504765f9a997df84c83102bff03289439887071ab847956680624bbccac3045f7b247134c1427869f007a45868d1b6c377e46fb04dd4d294d5f15c5959f35cf561c46d7bad63b4fb4202bb1f82a6996baaa62492c203a7496a003aad723141074337bcefde5759f66643001db9f358c18802a2c9c6b4bbd17bf79174765138e9ee044281fb56f38bca829f8dc591b40575623a8fe2d1ad22d8f428339bd424044f838771e0a4474ba79512f58843b2074be455b08011322bf10111065fd2e0687c8357ded884de083873ab66bd211ed741869fa5fa07bebe7bc6282fadb5bd5232602653b2e144ce9672a89167354348b0d7d825c7c0163a9da6a90ad284e1f5e8521eb993c1015065c3766264523df537f52973792166c7ed742ff98df704b6e5d38cdcf4163d71b315f8777485a3cec162f5b085970f27c1c345dff8c6a120b0a41fd176b087bc4170f08d5af373108ab03f18a373c2918c643ec4dcf35ae548f7099d26073cda36d4a7fd0465067cd0e6e288d6f05a6b0222a40915f73ef332850fcd76f2abea3297e7f055270385c994cf8011f519226a7047643e07b6a5662473d5a4e29ff36016b3037c12e953a5328936b3a33aa136133ba51c01181411d14c1bed41bfae5445e0413b0710639d6055a2e775277a1537ebb374273c190f3609383ed46d10df1068852a45ddb8afd2f64934551c808f70b50c33e5f50144f0e35076b0caef205157e939a56bc8c6366cce63057b395307825cdcf7896bec84fdbeab24c0ff1f77ab7918a3b6f137f743d456d5820e52b29c8156f427880716b2d7cf92e6785743accc9701ff3994a72a03ad556c7fad27aa374220cd5e65913b0e509094a2c1c584488f448229527744fc88372fdc2590853d1e80d992a9f730642cd00d59a6d5af8ecd95cf397ef4ebc695a24507dba67e9b6906a322ca23867b33c7ef1435308fe27c46d65b0e575725c1d01582e5318a06aca03130d016802c11c3b9eddb064eb268e4b43b0621062ca346bda0a753bd2451738c131e25eedc6fc673ea8626a08043837a93f0f104248cc00ff355b14ab2618236789d14a7fa7c5359b2d400976ceff28aa7a5c3f085fc54578d73c184d99f92d0473f935310f1528f29005296736685818173320d9412f42aab55252a9dfb74990335a7ed36b114ae542f35cfa642b0155d4080607b7b633a7cffb568e26b700afb8b660ea618d4499b6a84c57b7426d71e8cb62a10b01605f8bdf708e43fc408d91290286e9f37265ff060300f775179466b764079afd611b4edc56d644344c1ee3944f31cfcf7946cfdd76850cfd54b0c7b0620dc2b213c1815e44577f014b5ce7b20e2f85904c9c91cb3254bdb869b698095d8836521768827015a505ee78ddf1fa14d913fd33ec51300684bb836a7e37fc4468bc2c45ff439742270e246592a9fd4219802a597a5914526d888319abb9476ea833710f0f20f1408c7a80772e8eb426736b7c62be891f1e2439f857bfe31b392e8dd47b546dc943b9a4e56970126334cbdcc81396f14457938d8b0e6a54c05efcd55a6cbaca1f6ada157e6989f8db3e01ce500dd85e7f2eedb322418d4a14752de84865ff1e264d119ffb11854b022dcebc9525bd7f30000aec47560477c7172320a9306c917772b5cbd9670ada715623b6d66438872e75650a39072ec0f81faa26ac4fe403554a60b42c238a22d34400810d6747fdd61eb93bd24bd1b83660c8fa6d0d25be2c401aaef43153e459637b22185bca94884b8555b5699b600f614118027a4167aa7c9f6521236275a622fb93be0482c97466c8b4d15bce70961cc436ef4054cd7e2ff8b9f3696a058d3aa8d12b32aa0eb923f6f72773e773873df91445375e5fe565c43f7b3bd4f99d3014976d5142f4b14905b425095236e2668382711cd5a82b3694780c474b04c15e00e6367527e7a8394e710e5c872dcc61f630a548fb3f284e76ba2a0c1e6c276551b673240c39f90218142a63d2d77c3085a6da50a36c8f429659204b9c1b004b86998a5ffd6e6e7ebccc5f485300607794c3a879c3371a1b3472eb1f728ee944bee9446f741b333ac0eb5c6e52efa715d55fb8279971773f06d4565316b2830e882afc1374f75f509d45321093d62418e0079861f9942547e968b95f02e40f6dc1bd13091470691d36bfc019e840d253f21b9c57d8a3a75fbc03d94902ae5063da90c416d599f82bd4c75d462f45eb05881778286148347188b02b1e399d746fa978765888b08478f6635b062d133c64ed52270b10745c4c5d4b0823cfc07d1045591577611384244fc5b8347fcda30bf74d8712a0892a450db39a46f48ea144cded29722279137a85e6e434df1d9275bfaced26536816105c1571798f5ef720d70c333edbefd04498ed213d36a6e529684ad724f269376f46434120f81fe0443f900515970165429777d07bd4e2195e1383ca316e7bc33f91f6c109007e835ebcfcad57efe8b64e7e7f0b7946e41c53d3440b34d978de19fa49045ed1aad314205cff593ff3ef4db0c6b91abaad224634b7bc71c7257e056efc4f35cf2ccf387c6ccc5dbbbb5248588add18453c765c5e1a625fcf22ef6a14714b1187f926735e7e417bc60a6a28f780e0302d24d530215c76632cb73f0c61b82d28698392771edee01499ea0b798334bc6b35d26a49203b4c6cbbe51814eac39c75840d9370132f0d47cafe6a253b3a363b0476ed4c094f6b3dfe2d68565acf653e8817555cb65f4d37d5d58265a56299138581ef32e86a4b19fddfd514bccd1318232b9e775d57a040a07cf42b7d812505aaa9733a3c2ab42b01b1ac287fed9b15cc450e33055f18549217165d31c234423172996c267aad5934171a5ec964974079e10f344f8c39281afb942f4c8f771a468c4a5046b7fc2c8a20d92b100ff104c1ac262f8b16fe1099e5db1260571b034793fa11c137c16c35dd1a6f3fd7dd5f2b50c579292b037577c05f20cdea36154223ca393d76cd3f218c51688fd6033823d4fb6e7cf0b9265de20e2a2ccb4512a09ab64222f2974d61a3b74c2cda8b6e4b11ec122bb1583fac94b362ee8c5245d2615d04d747c66b939b611dc5a5495f2f6c226bf7a1ce338fae73513fe1a25652cfce3a78d7970584f4bd211e24873a4435e61fa6155137fd7f8c19ce3b5c6173e75a429d486d230b40f81aacff1c33c14b2046a928e52e07968c5570575a5a6fd539225a1f7009a8dbfe78fbca1076f52ce61307e1e40adbbece718c8fc6712089401d6ae95a4460ded71aae42ca73a9b5d840d3060d2d5dfb2367f348300d6113ed459862457181147674d48b7664ba71c24b8f1bfe0316ed1e4de7bad27742aae53b2fc2205000f8ec4dca2c16128999d16df7ad3a2f5408167151f72d1700923364a357543d89791e2a17f6642a84bd701b70af933fa1baa76361385b45e23c8a3d9b86ff67c1bc4f261138ea4c151b10719882f1016d808d1bf46a702a08ada7132ec6423c4639824137c22126fb90317afd592f2e2a9d8b4701ba15474cf50350842424105feac257f62b1b3135e562248fb53f3d4242a6260e9ae777a230414be4005d4631935a4175b63f0755c14c26a344894292414b469ad4510c06088963b9323e11d5c39b035d2718748cab6a76b318a034da87245b89b98d59ba3f5f69f663e0293a70244813f2af346d7513762074492d843605588d12fd181bca3c2f8923bd3c34f9596e059431107c72763fa87c56250c8188024bbb917477921843085c930612c877604d5d6e7bcb0159103635e324f400c10e3caa0427906cbe6b3b05364a93584d0222459829dc3e8d7a0f9ba5363341ae2433912e0ca8e9a41a202bd047411f857aaade81749327582abaf44803a39f3e2d8837cf7a1678190408f0c56c476a0b707e26922cc5669b5e989f7113083a0c1db56a274afe41995e67ec1d158bf94647e52f97577b5c827508d438336c5afc0dc7dc1968c6afb53b5436e16e5738be4072bfef25538de91c65e67751bbcf3a06b236b76ef1811e77b82c2f2cb2a8da5efd590602061da0083b283c281a224b30aeddde5d1920fb7e7f147f0a3a23cb46157a2f4d78c925037918871c1fccc72806a1463b4000c40b081f2a61b33b0173a5cc22392dc35f6d6274e212d1d76a6a75ca34463b853e7cb027be64cdd2721670a72022b1c02f7ba2e6ff661bbdb37b48bff7760d3dea1df1011148a0b75e2dac1ada33dbc0294579093118b0037668cd26011c45e17c45c552bb7eb65c63757962903ddfda6a11d8330f6b1987790883382c5525841077a0d0b8449ac5e833b1f3072f029b2060855da9073ff28e29fe7efd2883d6020a9dde0e65dc4424757874be4036f9c475e93c37044b740e4aa2dd8050c5680b6b6cb7181e389bb2646a28c659465bdd58607ddf07f6e11f185637142b11ecf71eca0c7333c3ebe1793a68b34af02ce160f63def1aeef82434a361b445b1facb3141e3751359a75c6ddd84bb6fb3a8f25948967e3dfc27c7680afe410440b9ec79a9dad3099f98a60d866cd075d99c3f52586bbc66146a9616c59c4351e055d21e699b944052f29a4fc01ce80490995b32d26f1b17f1603950184260656398fa46ce46fe300fe6b9665976d127a0d2ea490bf57343867eb67a3aaa243694a1045c4ae07d39ea656502ad5ea17e0f881f1a776f53026f696f25cd1d612413fb3452091adf2881c4721c37a79e543e246014a1c593676b467a27d1fbf63e5941286a11426204f6decc35eb6d7468b7a720582e80f63aa767796497b3725abd4f705f692a8d12d9edb01437e47e2e5ff7933b7b49cc661491ed6a539dce2c1eabf713d9ffb5703aa9927b705d44604cbe036c049dca07ca179c6ef9b75c05bfe4e159debe955cf0d55f104c28bb73ef34de5982ca297993c25348aee7f012af455a0cc8d89e243c16a949844b9f1187829c7eb69605190c76804a74f47b55ffc29f5f83e4583b05df04190d918a310b483051b1f89474f6124e2bece86047c7e2e86a57106c490afc4e1628aa7e76557e9436de7dc2793df2d000882fdb5f4ac559391f906535f2f4501d2916c849a5da715a5320b14fc589ea4316e7a73a2297697b5cd4d47ec85e5b4728cc56220995146d73249c0c6fd4f17b9ff5db2f96a5c209f4114262cea5567c4fbd3e7c26e30256066ab41ee477f456907719019b4311630bf97327470fc055cf7f2d4f07fedd60047f82189ef7f3530392bc484ad39b00d7ca6a5922dc276160ff300803c8ed32b0723a56b197d8761634bb1314baa97b4a992e4d21814254a668286c7814a204f83dd77d07366f7e68a4f117de4b2a4877ffa454bb4fde79cd5eed2f88f92c680e82a055aace664e9abadb7d4960e54d4f2fd368607f156469adc55949b47f7e568311200b21243846600027bf4682094cc8864c9f2a014f89e35f6500fc236cc329441668f8e81b58a3ca3c0de736414cfda81b3a15a06c353400255aef952b5c16db3d94945c3c5038b37ec74c4b5fce4c2a11925ec92e3720de0d7804863594eecc33f8bdaf09551ef94639116274af25bb404b5ea36c32b299504822fa204499756fb2f64573c6737209899153385349801bc696b011512a9f7eb2acde550be2052c07d7952f5de2ee27e7282b5d2bddf226e7c6d14cc01c763aa012c7301af44b7b2fe5a87016fb3846a63acb0fec224933fc973f73d9f5de501c9f4f264f04b44491ffbc595503212110cc570cd90e4715252e3a7bb10fff02b7e5873fc2725f2fe5a6e11605a1b05c62a66450412c2f24a630a520ef7a1c364329c210891f2d0c409ef54149839f2516894d27d909ed481e616727adab21488d7d613009ad452395763003dc575e2e0929d233456fc478c7993e5c40a6656cb57eaf3c734ea012800c37290a479319bbbbdf555ffd3876b900c32167a32829b972db465cadc927f7b7405e049566599f54a8312baf9a1ec308bd01a7d6920614330e1f3fc8cd273627532c56d3b839e3c428216d55b2479f8b2868be41c465ab09247c2aae42211ac98151a051ba39bc5a806f2ab57b2ba0e64740ef7e3f5518df2259936b7525bc8a9965eecb1448b568b8219b640664030d801c563fd748060c1c5e176c1945cb94323677f4f725e98487227ed11364e643bb113fcf3b58b7e9123ef5176264bcc1a11350fe7222a971c05ce82cc63b54ba1946935f6216687d90125917bd639e39d270d7d95e60c91e0e4cc63a5269053cf37e699d030eb803a735588cb928f7e6103f8b3a193aad934336d107894f55978665f3c9995419f9997eaee3c24e1f7ba60b74495f4b57e2cd375e93c778dff19c14d7dc0d5cf56ff86f1f7da958e570fd17e6e08651a9170879fd5f3d086f3aca1a71e26d09c20fea2e5175d945be7cd1750f04c73562d1891c3da085089177f348fdef4f542b49b55daf08ba405c63ab62d90dda770aa6b34ff8384a6ce678705c7807e40089f11030625f4463d374a74a3943836524a2d57a2f16ca7986bb8513f787341b01de115c0bc3ba5b4a08c02b1eba965f035c924e22c85a156d652f65c7cc184f32f87c6700056940b4cc0759ec12f71a4a8d6176e5192d42ad8c8e3226e6773d7a75337db26f810ad76ea61c9cb0d765315422003571813b5c60e41b5e59722f829d057110a7455b2097457dffcbb84e9c75d25cb0fdeb6d99d94c75b46a26143584c055d7dc134c0538125d4bbe796d48ddca521134720937622a052403c1350cc07319327757759a3f6609b44dba49c034f6313ab5381fc262701bc695c36285b739236a5077631e83cc56701702251213643405ffee50f3858a65ec1dc2036d2f943008d72e4f53ed9e189082f61902b76f4a3eff942c766aa5658e36586222ea1f6eb0067f7ec8c6df148f94986f2c445a5de14cfb3b98644a16468183423177e70022d2b8621aed437ea2438f4bbcef7b7e9865f9363f5b2f479fcb5672568cc9092eb7b42645a5357df23ed8068e8569301cb4042dc1c1824098ea2e79911b5f054d6a001697302112ccfc0b621e41061ef9c4e777a737453b76e38b7e43b51c7a2c576f3776fbec1511150d245565fb587d3f1368b248f10d5e4a942ce3b1c50052ec595e5984d30cb2913859f26f57435620e54a57c0d842313ee370625c3f713ab73b69ba661c3d08b7b154e28bf46476450e63fc6e0d1045b9c535f6f9f36e777cc404601a576968ad9601bfaa2765f4c7603db3d83318137313507c1896056a56051102738e3d4c573158c1b9f36539a1b97ddd70152b2f0ee978094e28755034016ec349b00fa12b295075c5616d27ae7d7b12eeac16890a1b6421ae300af20fce67c7222c298a91a76cd8b3127de847222d4a18cc1f463d12387272001ac68e725253a29354910c3b68dc54db7e414f2f12de81514dedd9c06191edbd3194607e436b631210302d0456f9a693303deaf871b43eec6175657d2d02a38c088ab9db2a5c97b001fe782668d8ace34b0bff412e3ea1187e93d58c07300a973f50cf977ba0f8fe703143c05b1f56e516a3c4f84f35a1283653360d14ee67b40d4e8ace073c6ce31fd1708a122cfead4254c1725fbae2883bcd35cb346e804b7d309f5e60c07be76948ea4b4dfbb10318a71b82486049482cad491420b10987068c538314117cb80bd87b1054962dc50bae3e7615ab67be0ef8ce1364ac27110089c0a500a7a90b066c36ff2af98dd84e2c37c545a2f79f5add0c2246bf26b064cd3c7a012e1af2184a3b944b8567c974fd089b69b89e4a002b095363cf357042e33ea5201548fc75065a15187c63f9361f27dd3a0ad61763be40d14e70b28322f340b70f21f1b56c1f48a17a134ef51e8c5b410e0aba4803d0505050b1862771731c970b04a23d71215c85185ae1830fec43061c2babd11249ad4846831f8b5d7ad4fe3b7cccd45078f8113b9c19203abef3a27ae202e87e7a6dac2feee8d26cef5a040f6de4fb6af8e4c77a834f1c0f130eef185684c3310dee061c25201410d8b5aa7d2023e067a70d434da031911b25cbb348ef2f7e10388ce424c89e64219d5bfc5e0c97163aefa8dc6d088a692951f65b5294846423ead7484075575d08e14eef782d23ff64491e0366751f5415cc2dbf4c62b72c453dd46a0a9d88707330aaa610d7e30f1690ec4247b7aa0d64a7955947e33f57253a9aec0343895e5c34210d1ce81e523b69f3ec7e845d192a7060532bd093c8053a51ef233cdd7848754505194792354e91ae6d19fef04560400bbb258bcd144ef5f767714c45d76a0269653ff6968a6ee43d8b373b2cbc7be809c164902703483107b1317b45e513c33eca7433cd7f44ff917566562e303ba20d1e55a19bfc0c90f2f66423cc531ef5f1124545481b3e5579af7128d8d807b81a5b0368408a2bbe031c5625f76b0bbb225835460762304b0418656332750ee0125f5b73974964219b6142fa9a2f453664b759b5a65275fdad904bc72534718a93ba4e40e5d400f4c3f726cb981733939c88645b4c891f7bc03743e810de0a70d51a35669643467d30d766394b8562d66c4d3b6115ae7a3800613cd24bf34d8fd8d33e75c72b231329ae50a5030c1a2fac494096534139e2339f0424e5b50ca4987015de3c0a7009e4480cb439696f287877548c0b7c7bc8105528e4292365dc553a437669347ecaf31018f1b3e4501462187786de8069bd1df33a813299126a3ae1367bf3a23bfb415c4ab6261819d86d416a9879ec020210f0051325e445013b7945154d801de32a3948ccd4685742b1ea66e4bea920624ffd42c1d00a1ec1d4a56be6544f1391bb332241dcd572fab551753285df25a2ecb519562832232b4a021f8058576506105b5827722756713b4c7799ad18274d0206748d6c481fbbe34e1db5077a08bf659d77e3dc2171ea368423d2f351451f010b524b3fee28e1aa1878efba552645a02703e3f28f226d55b4317050095833b79e26a33609380e0e911e7441c5771cf31a32a172c3533c5198376415a43c3a02b06e83147e79b7420147ff56881c00f0562edae5bf7c5d62d24183a088061cbb7d1b2a43d50644443d2eff251e097814931af5bbcb40b9305112be69b265a8b32a2fe0afc209d6cfca59989e4b3040ff9b4ed03e7e4abaeba807a729952d027bc10d1e4ea7606861ff0623e9a154517258765fcf3733e4ab4f7b60fc916b0d5d2a2c3f6a1b3e4678725979578d7e8177af2a27380b7b8c429b5829762152bc848867f57b5d4e97723a10426f971458190a24dfeedc343f029e59d50aed38a8d4855a0092d354e0660034b5d94b477a54c125d4333d163e7d3c514cbd1b5fe6f5075463a1ae4d9194d3468c34225fdcdc5c38a4f70a252b6bc00bf71a7d15c0340e5d53aba6361dd1ef6a8f242013e229711406e5227baf3562495e55f070e6cbec28db314e3915858c5c21b284791c3228390a707069629c3d66bb2a0b37e72bcf5e583b245ecc39b966ecb07e6f8d9c100227e6e7651922f7415d52893158d9990c3717443b326b891f14a2e465be1011466619cf1d1cd25e0cf6846a33d2b57567d5e10e7a594caa046b291b5539c03f75b5d81464dc04c57acc761c0f60fcad4f88d3272ea123e21f8f0c3e67751a4a0df6ce5548a34b0f63c42f686ad7ccce1754a6c67b086e075823287a1483abe811500d402fb8f6c35e56cd8a277cd8a843eb4aea2ff23317509a2079162065512a9fc2d36a2b74950ef0517b123fdacc6c78b4447dff4e8c2b955d130ad763b477eef2597cb666415a126ae73292cfcf260c6c9c58eb1fe17053b8194072eaf61d71da1c29667006078a94ee70d56ad33ff1f93a7350113833cdb5eb482acd1134e2b18e0610445002df1d8958042b21271764b4799d16f9172f4a453671a4dc6bddeef703d9c75d4f4772c16fe61c5d43168def13cbd4ce2bf9649509f726063554d68922f736447b46d0995b6fd07b4abb8a823903784e2f216f920ce496e635ddecf563899f5a45fd3dcf208e8e5664b1ff0d06ec53171eb9c39b2939fae645fa4d556131edbf6bd0ccc627df8cb324a93ed24510d5cc105c598e0daf588d0ab085cb0b2377df4fc2c6820fe25986262d8e1452a7739b2a3bbe1c768290417ce4f9b0488e0f6e02b43ce86c801a7e5b04ebdc48892f9b743470521a4acde02d629ba0714a4e105e49803a2148a1aa12ea0ba659e91d7a573e9edd0f685bac5d42cc7c60a5b4170914f9721d0f034f2e3deda42b9b75be426aa955155666e8403f5ce4010e5dbf00b9dbad34d8f18271505a194cd3312538cdc6142ead31f6470957c364a6794c499b721712c4f2a30d409ce7049ada1e296228aa7ae91e8f31094a6e73e3362274319c1567fc36540b6d229137634c3b0b38199e6d29cfde419af7904fc31bd56ff3c9061249ffb80df2f8fb4b0d8157324334de542d051364b8005a70da22450467cf88486e63271454f9901985cf1a526dcdaf6f0ef2a745000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b70244c04d4968a9e1062e32fc07631c404c65f483315f312f7525120df95db3bb077847fcb73594d3af12328bb52ff96aa354ef09c73d8882266370eabd4c4eff360e127e96075c9caf37ae321f639f22eb483d6c087d66e9613a67e9fb13113abe4e2fef6a488331d35ab939c6418138226df2dcfd552fef6a488331d35ab939c6418138226df2dcfd552fef6a488331d35ab939c6418138226df2dcfd55d4221000de1fd535a0d36f2ed49fa95aa2970851b8b12e42262b9572877a964ac8cbf0647441dd7b977c0d2c42c9386444c1783af0532530d00f892e6fe21b61e977f0401958d744dd2cd23a64e354158fdda215cb48396e88ec69261a8f5656bc919f38a3dd846b741fab309b0dff5b15fd356276b43e6722209248377c0d10beb33a1d40e8ab6891cc7e7880490523e990b23e9ac16345ec12a57a4af7253bda2b7525bda52c163eb757583d23aa37ae77d819be52f7315cca3219747b651534a24a1b7b877d11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061eda1468361db23b4110a66ef95a17a73bb79436fe21b61e977f0401958d744dd2cd23a64e354158fdda215cb48396e88ec69261a8f5656bc919f38a3dd846b741fab309b0dff5b15fd356276b43e6722209248377c0d10beb33a1d40e8ab6891cc7e7880490523e990b23e9ac16345ec12a57a4af7253b0300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000002df9c4228acea600195e57699b163e371a978c440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ca8714dc779e96a0331f51b8fc4f519ce4b4e0ff7f2083c2b23e03444900857f968071c4e13ac4b2429417eb6a6df4ac9ea9223bf45e73dc9ffe4052a0a0c3e04a8067ea474786661b85756198ca85e04343c29ca8a4b076650597e017f2946ff0dd403fd3928198254fe7eb1e20740d66e8e32c76c131ffd3928198254fe7eb1e20740d66e8e32c76c131ffd3928198254fe7eb1e20740d66e8e32c76c131fa48a785db9c4cb4a71c1cc31c4dcc76c41686c6eaadd8028300ad96932bfee47ab69df6959aa765c1cddab5bb678cc2832f0bf47f5f55e23054d2c72804be4715805f259d4186609245a924704e0285ac0f98970ad4d762aebc17e72c74225623db0693d710fe350cd3b166d65b92c5a99120b4fea9a42765246151d3c2dd3359fefbc066d65334f59710d2a0000000000000000000000000000000000000000c64751513f6230192eaf01602687415e253a0d5b571b651d2e7ac71679cc2b3ac6d50959bb715a0f4dd3f33a3418350d000fbb161ecfde48802f5f32a2c59311ef1ff26244fcff5c25cef15ac4dd9b1e25e2823748fb2a6e703e96484bf1817d618de540cd924444ce5d964c1811b26cefb1060d7ccfc37bc842ab6fae14032d764ff173a17a771f2a2bf7476d255e48e2c96d373a9428105885f94d5fe1074365c6f622a9b05d3e8027d059b6325433c402715200000000000000000000000000000000000000005df81a460a4eac2110e0c030a39dff0f11a70e1b780f595f91c44119cb19dd6ee5425204b0a3236788959b2e2d6aa84ca95be3440b1c2a4679e510257714cd7081501600419b9341ba18777e8edc1f33a9dc49010bdd3e6055de4f66df50e759f9386867411f8c120c3451629fb14174d5f3ee42bedea3348d9ace3fe95ef820cb16c7423624223a1af0c00619f9082afe9a8337a8e00a58bf1c2b7e67cb1562b2196219a414ff360e145a721f958c534dd7ce700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a52db7c23360c4f572ff26e9e12874e50ab6f1edc9957264d04646f8210f0294880c71a00ba372982e8dd769d1f1875775aeb525cd5ef159ce5db44d756bb0e5df462676d2cbd66c161cb40a37d533fe00f603e15fa364ab6fd6e0090f888398f1b3f04b5456408b428fb6fb5d2540464518048a5f05e0c8f7d3d509e55d8663867787140f10077d6ef6315232c8b5d7f7aaa63bfb25c3783681068c3393155b0ea316f1fc7dd344e7d2b2738c96f547ef338083952db0d23360c4f572ff26e9e12874e50ab6f1e3172bf14f98bd85e0ebe526d2027626844f296071d26834d7223ad00318e1b34df698f71c962934167f40a696f20e514bfbef55aa8a9067a3139dc4607e73c7aa53603336f719e40bd0afa1d0e77d90985028b425cd3014534cf2f669fb7517374cc811d35e82854e73acd79dcfa9a3e1ed1cb57272add22e76d0878a5bf98268567e0138c400476895d8620d2bf181328c3372caf1501465984bd410e0b5816f1e51b07cba9a413af9710124ef4a47713b8c312ab1d960042aae62c9d7a7b2defd4055c3ce7aa3cf7cef0743947e4159d8bc324d3f82a0bdf9c1a5d9624b42cce5e5033fa1da236ea00e756e852445b27de8b32c747ea3dd1f00157ae0f5b21ccf05e33c29f9707d2fe5f4cc943a47c8b3d7d4f7265243642e6b356f3f8c110c6d79531281b3b5b5a0be91aebffa84be2bc380e93360a7535e6891f7aac9f3cd786fc3676024f7023ff3772e2889c627ff99d3ae1f97047e3bd0146c9d662234dbb7e556289b11b8df1b630e23c7339d20dc25200879e79f2913a43c426822b90c0ea77e30b0460fc8b5f4666064532cfb8aa251dabe927fd26036205e90c5faf565e6ffbd13d4435042313a5e19b4b8894197a8757b94aa5c35603563bf03d7545936104af9131b68ae26f806f3657c99db46e30645b022d4c99329d0d560de87893411e188a2d961387624630ff1885106d0b2e0c887e8566927ccf24bd6bd675130883eff63ad1f97057e3bd0146c9d662234dbb7e556289b11b34bc8413166e794c0aa6ad1c9f58d84661aa910aac380649bae2661e5c461b4e1475ec057f09dc0f9f94432f25980560e491d04286e76e24d4787848643c27771de4b2094ff83521d48c324ab6016f27cdc617180bbd5c0cec3e692a92fece1ecba5e03537330c59ea4f025e897b0e134600ab1fb9ef42036370856ee4ec364fad7d875671596d5feb552c48bd481f251d1af82738fbf44d00e7467302ff042bc1f97067e3bd0146c9d662234dbb7e556289b11b6488cf56a14c6968b2cb63287f32f3439d9e3164d9fb86156d0f7a57d9e5b576fd9974570f6f1b288834f36313e74e7b4f95e57911aa324cb831525ede1992435f36c0736c2474148ca0473bc6ad045631ac1917e3a00f1a1b97cd21daa3a35019418d2a839a127457cc5d2fdb981c1145666921fc4a6a1e1771385fb4b9d70873d9075fb06bf924dab9b50dc0d0694717bcc6640d66e0299dc0f63dffab99440300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000008df68f69b4f4732619e73a099ac22d59b8688c2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7120c5b63745b71b3c2373d88b0314704aabe0d5ef6a755e3b58048ca59665a01cb125e256a5823033bd45d9895044916cbb60ec561bf741f740b51d01f4447079fdf1dee8bb6796cf6147de6b3a2733608bb7e7de5cb22833da368191bcd477e7a6a690be58d72e382db50a9c3f459c24383564580c27b0be58d72e382db50a9c3f459c24383564580c27b0be58d72e382db50a9c3f459c24383564580c27b0300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d00000000000000000000000000000000867fca1efa30db7395083128126c5a774e1c234a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e7315738bd10e524af90f661b47de3f42ce513e8551823a773beb3825894806c23f1f354f2489135dc0126778e12069c104584bd23e5a44410f435e2ac72d59269f5e53eacbe22848da41745a37e52840d1253d5fef0e03b1fefd279ae31311f7f1a374341054413ef44b31d2ac132dd1b4a11f1e95bb6b341054413ef44b31d2ac132dd1b4a11f1e95bb6b341054413ef44b31d2ac132dd1b4a11f1e95bb6b1700c478fd7b9707d5e5451432b48850c1d22829e6acc7630b731c525d4e5d100796da33af293f7680c04d32e4cfb93ecfbabd08d98e0d607cfd485d441f167237cbf043702c4d4ec7922b28d167c370768e277da9f20b035a33175c03eec81084ffea7541f3a61201ed4e57386e790c07eb710140800c4c3333900c9fadc644046a2956586a6c217045136108218704aa908d10ce87665d7b3e816e33279220af637c46fe8c852c0624c57d5a1e21697885bf240cc3964c2b4eac0502b24c435e4d0021fb62283c058e06427313fa52d23f3f6e8c4af629b44d1f0569c14a474c144e080352f04d34a04e3d7103166ac78bc3304f4c581f3e11d2359b76fc4133521614d2caca5796d4704a1cdc083efef3df62997dd1484e249318717da2572be396412f316d7e003381054d059903a048fc4403d8a21e75bd876e16b340526ef0c33b3f8e88055f2c71452850333c9fa23e57667ff74adeb8c60b7189dc5863c8702f40fd3f3daead16364e8a673e8a5adb5a696d64677b79e85a17e2f32835b80218da8283249824f31bb014303d1542394f5aa1e9280c76127dcafb84350e7cc17964fc947eef63627355a8b52c47954468db90b20b646ef74a33249e2b3e801b7ba18c622fa1867738de116f36c1cfe51ef83df70015677c5a6142e825abd5d41ddba4e573c75736511a28d5024452f25836e26539fc8dd378ace82b20b25d582b4c921f607c2a29431ee606518681a559cd6cea1ace382527cb93344f463fb22b10db9802f6c071786ca9a64566229e0375783b4532905a470d34bf0199d1ed32bf91cf2054ee7a21b313923197b10b5e9ec1aa3a35f0e8761a129834fb1e6907f3e2aa60bc1a280a847c657bae51915e7814121b893d2247ccefbf42e9d76750b055cb1d4641e946a604ac73db87d3356c49fa6aba0a503e3c017b3d0ed024561f4f001fe6fe9424710e943ae5be0c26bf4c482d928dd760d7e62c2834e440662431c92b8ad8e811699de2690afb606a1915116d7330725b9c80da7019910e433851c51eb721a24ac6e11f4bbc54322a748144027f5ec45d3099cc5bd148f73d1c8492542fecd75375713e467e3e433b24230e742231ae239137ef7a62d57e67cd71d61090dae3384333212b3f741a6cfdcc117625623d58e8e8812dc8fb633f2a05ec1d802a0d16fe5cff58426c7f3b04595e68f17cb608f471a3361ac690325fc1512b303c833f5f85d56679095b23fc304b1dd4523756d6aea12d7bb36e224cc80e37afa5b43aab9c742ce00ad41aa7384e641de1e179935910413e54482c5182a86b4969286df08ee1287c8dc26f016a8c1094f3d45ab3421710154970757b71a2431c2a7a56d2a5741d97cd391f612dc77cc0612f61fa55756b676c1b5690ad5c101d43c427ff3f6061a83eea27a616b92a270c2331f59b487ef7f25f407c2fca51c82a7827424c6f000d566f44495d7b0ec766bd2f4017c76596b98d762cefbd6eaea7f03ced0a1e684427f07826d2bb658c9e8d37d6c99d40b766ae479ac52235ba79807d18a3005d9e2c830e83f68d3c90bbc82b5f8887147cd7d465a69159617fbfcb0b47b9d839c0e93625ba671646b1c26f6bd9787006f9e5b9598dc31a5eeca57671a2e8c12f1a2e9e753eba01467ec0b1687a3cfd5dc9102644859457525b578204cbb34e25eff92b53a874c0001c565361c57cef59a002c62e3048f1653edf3c2f0ba57072510a3d76b3bf9a168449841fa891171a4a5bca6194c826038f836544fff08d641369da6b940a777939b83e3f60d8df39991c013cbd6ff12707e831217c8ec8134f33e809709d6e77b196451c66443101d5f71c46da2e9524f92e4e4c4b122f34f621ba272362f507dd30a7213ceb14566c664e435462410841d4db2e7ef0056f1ae27c3bfcbe2e006f104975e69fe328e782dd74ac5adf1b88991c3ed1c1d46c420e941db21e25260263b16b37530d6d05a18956af7ba656b85c0c37c0e2995614a10e7611d2da211f5e975ef5dda568131e9d0714268a1d5a3c6e5a14fcec5eee63ed374a3c357001d90848b58073666f88822be98574119f51ba25858b397353a50177ceea04206346154fbfa28c01943a9e1585b06273110f9150b962f01c43af25518caed6404b985430bbe524486404f770436f793cb2ad620013f03764185e960f1c2f5805ae44de073c12386d19463d50223c14338d3381417665192c80cf9179c0cf6e372169672059511346cb4fd93f1b31222a3b698e6354956518776548615196ef5629f92922e43b5a214938c264367c1932cf3af57c2407342fc06f1f31df249403d536456f4bba7b26e186d258ee372730c5632e48b60c6f4c2facbe12a9c3943b56410c002799b93ec4ddd806e499f92cd22c6c570e9dff374b3d017a9140a62a2f16084ad88f943b28ba6f0d3a459b6f094e6a1d19431c46cc02104a35ee1f0544ba101588da4679aaee600c4844d46dcd144d634617a57a70cce730847b363fc4184530a760e92a824b6000d1dcdf3c5c468045edb61544199e4e407c0ba01d9f9fc474865c1725245ee20e0501a766312d4c70c7f7cb7e10e38d018e5e0621e05df95502d8e35254ecc54a0131361322dc5e3f8c4678628b67b41c427b8a62dc9d53377a1b1e639fb5d67b1d7a7605c9e19002d7dbd2195b554f221ea9b22307f95d4473c19d4781824157e68e8e0334e509582fd7f50a49f6ea1d5d75ab644d65d842a916d1565052b60b583d1d3a07db6647d21f7e092f807815bb157971958c000e7e9e2958dd36b904d470081c2059e826dae7da4abe74d03786c29049ae9dac3cc2ee796e3c8d1e693873e7534c46c61a92169a3785aac05ee3f1d14f37537528a4216c374536ea36b4ddc340f533bf41b81aec2f53be7c4837bb7f4e4805170cf3730f6542be7e166a163e02ccf2ca42d3493f75622aa42b16c0212d1a48b3502a0f577347ea9c60f3967861269d3e6abf9dcc03a9b8994ae443c16259db5c5eaba05964d67e3a3c744a2244e952dd20d0b5872d10f22b1c50d0c3579b83350603e0360a5d432a371803b95d2367446312b2fd274b21c16b7549637925aa51398bfe104d55b5686e3852ba3f9f4dc32f6961ad792ad34278da9d3020e814056cc762520c67056e2c2c4a1c44c5a22b4ecf3bd419322f117c556a0e09e8b66d58402f0133aa2ccb61bf39472e0eadc72f5d389649822913071e768a0b2ce07025cdda5075d47d0f3f3a688b7471ca466aacf30c63af770f2667a8ed685a69390e4f3dd643b9b75e1b91f18f4b2cd8195d2d5db1640a11df4cf265d06cce4c2d1fe55fb8537c34824d29ebd065063bc10fa079e12177d8d518d8ec5040c109230b7308d2141ae4127ebb7bc43e10ecaf5991239242df6ad00338a87458ae88ac2242e78e267a5dc7339490262001e52b7c0a3cbd663e5e3c1c5dafab1ba4021348d0ca75127a0b911a5d3d1258ced4b32804b26f580cf14f7123ca05732a15096caf3e1a1c2632ee371f8df87c4a5565095438ea2a76663a0c758f3e67b97c674062a1945c4dc541179d94be6763e2680418b18549a74b90747be3470f5fbebb22ab212760bcb24a0a99d234792e0612262378c04917021122881f984b762deb5df1859e796c8c577b9466c72d79a9c7154bddfd3d534f4612fb3c913746b45f78348d2745f566ef65b323e4540c5ee8228b30214d27f9c53cd8704d5ef104b035f5303e4c3f190045bb2ff75e704d161933221b732a1dac020441b740e6ab6b67fed2db6a4a4cb505cb26bd35bbcb947cbd76b914d99337418098ae48dab000619dda5a2a9d86241e409d654af8d4e70fad965e310c4c275ebb506966f734d92feb33ad2f38951d017931e06ec8712b441869471cc6428c06510c2e2bcb2f58528ea99734d3a79901b3d78c568a878678c22662003f1fd634355301524c8015786d85ad3f9048dc5e54e9fa3168ef7659c8039d1d7a5e6f3e4fda6051dcb7800bb01ed94f31f6aa14f1aca202c9bddd07168afe133c7ff6231c235663300d7d41015191136588647b46822d19e81c9457fde0612cfd680b347bb5af797a1fb85da2624c6ffa61c33b59ce5332cebe270ba444f06942e4881dab119f2adb83510e48368a1a4e16ea7259dc9965ee53bd642361f50711a2b80b9cf70020d5908a156368f219d017581d2cd5c860e447ae32d4ed7020e2a7122773942034690a9041d8ee5c0a8f275d25ef140c0c9c8170701987117623642b7cbbe32b46c6589c3015613b125cef0365fb47ed43b5ee9a37b17b0f260b34552f4460816b5091952a2a1caa7c8bac6601b4317e03ae41b02dade664401201441bc6debd4923213b44985e681014904d45d8672d34d91c3508ebf6c30bf94d5b4cabebd65caa07a46e9f23c363e7b00c72985940642b47f2779ec0f63efc30154aa21f7f6250204a117203780c1e4be9562b47a019d29ba7544d3f1465197f5a14b6b514371982d57a5eddbf72efd2674d08a405732550d47555291936c58a3b15f3c8385391ee2c7c2154f360a878e16c2ae98f7bf172764253775750e3a1ca0f5bac2d01f6050d33f172764253775750e3a1ca0f5bac2d01f6050d33f172764253775750e3a1ca0f5bac2d01f6050d3335ffd5357986bf3f3fe07b1b7b0f2d3939013e54a72e5e016dbe0e7b3fdb87282e9e123b15b4dc76c0a80c78cc6a5f2f6db7d83ddbb9845da39b7d14a537714c3232bf3f17b4912a170e6505cd62714a7ba8f871777c5b2a3e81382f0fa90e1477be2a25eafbec3dee8f7e388a96162564911c5672eb1e53ee51d1683e2c2a321c1d3e204a49b056981dae06fd342479fe056f4e6ff4281f476df91110d5d3080394024499ecb33bc4a324631a4a7b17baed860ee04b5e5fd2223f37e2ed6169d6e25014dc3828371fe17208f76d23231961cb14d28b601f20e2296be190205089569e6a4e60ea0c39f43065cc16703617be3e1988a4a21c017ef06a1b67cf01c8e6d3585a0bc72cd79522403905cb702c9a1047d6cfd840ebd7160ec7fa64217bd2db0ed31d7b13a8957352ab7d353c8516b24a376c9876d7a906767aa03d14827ac24049bec074d565e20ad5c0f829869a0437fbc4b02dae0342238c33845e1a059d2db5da2767ff4eb915a2aac6401a18990dd15b6f645f28f91de9a139270c17e236ae232d36f9d16f2b24b946035231ca27f7c9e22de1085c0a39c734258c900869059ed017b98720195404de67a15613658812a6198b1f0d6e017c133f2b8f52491335ff307aa73c17b556ce295dc722660b60690a43faba089226cc7361316b6ae3d74a182ac3a1782722ca59db82600d2467ba7821d5dd04dd771827a362453ecdcf4533807b2c697c4ec61bd894e021ed7ab0271c2e4e6cde7ebe17887cdd0c9fced660fbe142199c88985a80afe14836879d2b464cfc07ffaebd7dbe87342f4e24292a7bbee412715deb181bed7e0444e38a1a61d64e1d168e0d6fd3fb51456eab783e4454120d5573d468583c77060bb99442e8b6bd14c275d83069ac5423cb8c1c798d87241c52ab423047d75d7b1c83301ca7f225763a54d86a4d98af3d8a8804540babe12f141b2b1dce18fd078abcd8503db9225dfdbf524f5acf235457111b039324be14644009168a37d51d8ffc636528b7a40a7bd1443d0b407f0cec79830b5272bd0efecf0f646e638e61adf49d1c45c2fe0d98f4b52e3397b8528f0be14c8438267b1b784f796af5df48b21bea1f9986ff2211a66d32310f8b715d5d42197fd3db07043327426196ee2d630c8f5e7cbdb62117f34546371daa7d6f403f28fe3db315a9ff5804f7f2c038920e08755501ef1368511d7922b87331efb21f71be1bef71f2de7033b88acd03bc23ce642f023340b67ef91ce170ef5a38ee77105b0ee35aa519a674c83c1774dfd49f28cf5c02575f48f56ac58efa5dec29cf70e4db8a509fce443216b6136dc1f8e212aa4dd315cb0c6d1acc431d677e152f00591d811925504a67caf6742cdf4b822267eb136877af2513039f953864b8ca6df903f332ac18115ba241dd5cadde667b4c80217b5fe9d122970f0108a39f471df93ffc6fc009c82f6fa3f653c8576c4edf8f822f4f69843ee45e87605b746078ee40fa6756e62a6fb8537e0976a94e3ba54f8f623bfd1e78285da6655479731f7dfd142d05d3ac36e6ae3d1594561a4d42502a42252441523c67fb1d1989f3760d98bd2993c2a646c4a02819a19ca50f1a9ef94ea6115a4cea649306cc18b144df897f7792b534264bd65e0c2ade3d5cc677bd3e79d92239af2cb62994a22754ef0e284fd024d020e6fc1142cd94e720058679181ccf3051dd033b3bd6ca4434d9422458d3f978426efcd72accbc1467b8efdb191cf44f0d3c3d2d6582d151613c09351834034132b3800f11a23b6116a6a6284fce12300ca373a2493743863c4789a363f2a5454ad7f7fa1ffe09c13a9d820c405155184b2cdd532b5595353a4dce8f1852e0636e955c815eb698cb69c976bd33f3ffed45cee6af070dc0ac6f0d8a267a0477a361dedb163f7a84317700eae61ed6e52551ba53dc429c49e63a73e8f612d46a3226a70c8013162e2e2bfad54c655b88e5559f6e1e5d4e63062a6eee7b566f80fd39d5b61b777cf59c559a4ff54b14164a263f395d7693aa522a126835758b5ed44ba7e9947a91c10a592a80712a89af6e4a7dca0d208a929d70f1a4fc7810f2b04c34571752869e5e7a355a6722d78f9975916db60a21d42e338c08296574a2861dc8bb396d42d2957a8cb9d37126749c21345209719d9f096fc2d35100b0c1347c5b5bac61d96650179a9d8f34e2301d3470c2244de882213b45d44e2f7dd34a10769b474a59c50c767041297e0a574d22b347dd4a32986404a6a5f976b4e1832c2e1af920ae47ab5db8248f453f2838073495db53e1d43579b2168907286e3b1a1443c01ec852832ba2483c77c0d3c8213cb7a304dd00856d60fc716b5d3d8f7e1a495a0df5b55605f9815f5ced996924fae0c30a2772986e233e8e43753ad16005c977197bd0ba3e5c286d1bb011e74039eb765b92e2ca3198ad131b77981516e3d4ba6db28b8517078ec507c3abd01dbdc9a045feacd20a68829e06c2ad710a9ab80a76979b684800507b77d35a371f7f12636a8ed8c25e7539252d8ff88f655b2db81346605b68796e975065c72a61c4be5f6d4717e7755820613e06c22f0f6c469b24fc6d2d26c51060096bc64800d987001933a8037b243d6421d93f91021331eb3b8950541cff50b92b09007e79aae01f6fad796b573f88cc007bfe6300f5cbfb2f4b7a3f5334e1105d46bc3e3f402ad5245966213b955434697691c031814d135e6f83091d92597d0674c43465b7d70c0b2342d13af4b962060171d931a9ade372f3e2da7c7828446eab8d2436291fb2632b20526433699163412cb6176521b17ea8c52737db02ab726839de254b37694506cd52140d4ca319c6d2712bd82c82793bf76f14bba75678abf68a5a03026a55df247e16fcb3ed0629e52337848f1e7566955a63a557da63fe22f23962f66d2f2cd094174aacb853a8ca170acf3e3a4b2738564a225f567e76b1c27848db7f07ad2508749076fb664782d139eb5b9f3dfef5e77996af4b0612520907281d0220d6e916646b6cb910e44fa91359675106e9a47b51cbd9c11cbe539029bf86e22b019f7a5205a81409515abb42aee7fb409328cd2baf27494ef9efda205c74455292b9641379fa6255262e7b3f2119211d4bc0af2d7e1f863e2dc0517a9485bb013353ea5104b91276864a2b231f21fd4ed2824c15248fba77004d1e75eab46e42124d0457bcd675513c0609654c898b578e6af757ae50e3207393f248f72f9d293338a0400edd2f0b324afe7db1d9d247eccd8f7b76702e7459ff1044152b565347a0b263736af471b3ed8154c7e3997c6db0651a09c27835d320e66b824a8d0bc6b15d0bb5f7040ec6d2ac6ba8440f47c730b765c7d288235318b65e5e3fa916141e06269069ab68783d356ed0c6ab35cdf863382e3ca1200cd03b0fc751d7002302b04d00000000000000000000000000000000000000008d626434b6cf062446ab02040674496e1f63a25cfeffff0100000000000000000000000000000000feffff01000000000000000000000000000000009362e720b87f7f0531221f50c2129718141b704fca28464c5cfb6c4460b42a4053409771e53126553445785cc30dfc0d6710112a83c69c5789046c40feffff0100000000000000000000000000000000feffff01000000000000000000000000000000002fc9a842cdbc0a3a9e783f3d9d07e65b55031237d027681b60a0320b4fb8a116f6c9823f3e82777b8d626434b6cf062446ab02040674496e1f63a25cbfd85814bb42f03407105560160bd24240a10d2dbfd85814bb42f03407105560160bd24240a10d2dbfd85814bb42f03407105560160bd24240a10d2db1ba344d7a1ba7747ca7eb7472652c5838389452e6978b427861e96848ca8a13e79f5e67b2684842f7a1d42b05fee65f41e0f63ab57341397d765a4f3aaab566ef0f356a13544c6cd2a4076a005c430ea1329323af041e78db3de50120a3ad5718cf2a7885cc5f268eab6a45068f886e92c32c2e3977af74818cd52e2ebde153575fb11b6595ae2b167a0175818cd52e2ebde153575fb11b6595ae2b167a0175a6ee781bcc6ed53c7dd568246123665b2ba19705bac658498277651ec05bab177b6dc84eb6dbaf7b66eb4d2b100f856a8d5ae22edc12983978fc294c40744851778c002cf6f4263f2454082d73856c1bf4000c69d476146399193433914ef213dc6aa71842952e083598c260bb71b222835bc62ea38206606477887b75d8342b92fe1128442b09589ff13e740e080c084abd2604e7807b71e30a2531e7a6b47235066534467d770e336c48342320527665baa871bcc61b211dd19b2bfdfb713a2f38ca603b913179762e22672630bd4112c05f78de8e350dd399ee276587574f0d88565ada3b5011c881d348b2525264174adb00be7fba41d2adfa47afdbd2397da83d5f91dbc23b80785b214ad1f461be96583b3648fd4f5c96586d38f332547d09512eee6ab96b4a912311e538ad243511f16da61f9b7b79b1b43bc929ea27622a416be79d407e49f9e868cb6aa72d26ce370c2b4b2c76c6e2683dfbf5031ac9bd7c493f16d46ad863b10b0a187c6cdcdd782c857df1196b60ed7a6843416dcd5ab350ced21b3984b5932984e0e92194f9e425cc8d5558dc30335c028be3423e670f6d96b573413024d553316bf701e0fd0e6c104c661e71f8314cccf32a5bb94463460840a82501813d1dea99f859a9011c5003428d6087b8bc6dd7538162dce0e96a0db36300a714343ba6f012316b14e429a218854bb496552ac4096e63bfec4727445cde672acf8876e19d0054be2335379821966faba27a574ae47461fed4e434cbb7752f38d04439af6ff57a4dc6da569eff25111076555b9c1b0f14224c68098adecd438d8a471ea1460d1f776a4a0aa1fef24bd06aa1522b227022e0fdce3f47b0e95e879f0b3b1e06595624e7b13ffb5bd9300152dd46f94d305ca74f7d1b83b5493a822a7457b82a81251862672b94a3c72d6025b86e9c27720d9376c00fae6f4069350f68499fcab759776e6e09e5db277b0d914849feffff0100000000000000000000000000000000feffff01000000000000000000000000000000004d05663abc5f1d6c164b610dd0ddd42b01134d5947f3801fe5a97c266fe7e61741be7d41c710891d74f2ab36405bb5726e72b16a1160ad52a2b5e251350f68499fcab759776e6e09e5db277b0d9148497087592e0d9fa71d7154ed052622b634009b340e7087592e0d9fa71d7154ed052622b634009b340e7087592e0d9fa71d7154ed052622b634009b340e2e0a331ff4b16e60fd6a8f6b7e58da3a7d9cd908dac8997b51edef0dd9719c3e8d498751de151b328525c9700e55c5613c00d5568e615e07d64f6c408adc1e0218182c6e7d2f8d394f876f2d88847c46de59140f85c357439708e445f9833a7379810a22ca999b5f6504615105c70a0ba9c53a6080c93b0a112de10bf08b707499776e41d34d93640a052f0a35b53c2774fbbe346da3d1509e327e393d02455e54a87e3b8c235c4287670c78cb5fb832f8429249660cfd50fee40217427d9a1091fe6e3c527b4b3eedad2d655aedd33f614d737173c81069c7874b0509179f328afd9b669023516603e5081afe4b7c380444202e62aa8223931802258adb5106f789ce3e940f73088215a76a8a752f0bae2f9a6915061a08a8aea6141b85153393aa35773a44147e7b8099722101ff4d534cf43edf99b6199d75f64c6264840d9827bc3e10871e0395762a4fd065085829723c6abe9af160f5c0b84159927c348fe6154b6cfae27e469d6e0a3384d77627d9146417c8c6445d97c944a5033e231f4ff62e044563166e4cec283b1ea600a159e215732e590abdfe5f52057d6142582d4d7602f3a60cb3350e1160452f3ad0365d5fb022947d18bbd33fc51c1b410851075fe24e2316dcaaf313017f121ed20eb809b7c2103513408432f9a7d730d951c50090903712a76d5a6a45dfa2653c587a5ebedf5e472be7a47c9f77555119ca9419db83d45ce743af1730b2ba701344ba686ec1355da0afa95a12686227db2ca46797df580c029b687ae22d305fe1ce503b70630507fe030d0faaffd16e80d425393adbb0166977556dcb43d12efb04d3722c178c02a4fda420c3b22a7edf80e247a47c6355ad38561e389da75dba54fe46ab9c2d59c7a2264b10ebdb26f4402a47bb928806d1fc542523205c3a1212d12c6eae276d6b98d8029cecb016339ae134f11c6e32177bed509600e5723ed1eb2c26a27b32f33c5e2eebbc861a8262592a8d70bc0ec2d5a646a1d8a84b2d32ff7c491c2a36902f1b0e903a2010c34c67637ecf992c029cbc385107f54be54af13b1bbc617a57448b1017f47978bf4ff063327e56729c434a15af91cd644e5914686e622f5052439e209343ef7b710ed969000137736cbf1223267ddb3cfec6710ae07de9346d44aa2eef4a85463e8d1c586b15c53b6da8b465d6cd9d3aca17393e5238d3397d3df434f33f1223558dce3cef6a2b1d2c5f8d142cc6d575b2fc88310b65a01c169c1a5475e87d684b1d841005e5991f90febe774285246a4aff433efff42b14911dc82f5e7139376a07ea395bfff809daff687c3f871b5835aa95754ab7cd66e205f564d042af388bcf2c7391b0b337ce3e592aeb59a507e16f4a2a1e577817ca7c7e577560c2469985fa0625a6062e568806541bb73d28f726c456aece142d1d140519a4cc435789ed62708fc60e6741efb05b95b9797ab6424d08b893433a99e0000c7e4e5b4cc4ff7a69b495281d86db4c5debf9b02cdb10be5f677bcd54c205670dd6c79b0733be5f5849ad822702a6ad06272d4343095b3b657ae1c55c8cc2115018077a65177e573d3b68982cbcfa162cb2c9e36167528d083fd357287a4173335ba5e125a4e1651ad0f6b937850ead01695da33542e6826436772628de07e02b2579492968ee9a7a2daeb9475a196f0f43e97b4d9a5cda393684a160d43631733c67c903a6e94a2901a2b72ad868a92ad2934f28a9ab6b5c594f092f1943971bb14d9c162aa20a7a9a33c11d80668a5ce3c40e4d0cf748180d54570139d25f6fd657db3890b3f65f5a34ab08599c0d5748bd3d1935462c08f09bb22f9357c842c62643339fcfcc57911aff43200de6582d0c46361666d76ed2bc431e998fcf36d1686a03b3a07255146e251b0d75861d17f2bf467f956e6e9f05b81cee76254aa9bcf6726e5aaf58ea79082fe0636d69cd00b5741db9ad698edcc064f26341352f00b02bbbf9b40abd37ae6bb067a84c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f51b983245eb971bf700c3645a8ad8586c6ecb4768a5b917b8b224556d3f7314c7c53b776f50ab1657835157a7dd7a3b7cbeac1166359f2cd26ec439067f7f64b75b825f76a7487c2f1a5b7de37a1b63ced4fe136859ce737eaaed569f440d423a4ccd78399aae1ae11fb45cff35114cbed49462efe8dc4a8107362e17a49a23a813dd01cdcfb926ffc5d214a5557e2a3d527120f4d37d3e702d47171b390e1be374262f7ad88f33449cb20133f37a2bbdf7562b0bb66327934e7308e9d2155c7411f71c4fc8905fb9fcd3449013c032cc955f0364ca814b108c74620000000000000000000000000000000000000000669ca64d5b9ade64bf794639292e9c7a6e358727777db01a58de4f51f2f2ae682722591657e083647bd4c075e1ad6501a2910371c6a03d31da0c8e634c3bc965da34bc283686f86d50dff550072a0f3e8a7bb71a4bba9656cb1a1827a933c2295de3593a57a24403d93f50347958f203fe35274924715f2934ad82222bcadc5dad34541ea29198211848d34805579d131f8bd258dad5cc4a3957c14b01a52c311a755348704c124b367c986579059a5bbbcf3572f485ff0c5c9b5657d0cfb7054be80d77d5b90a300ee308447e9f95567248315ff0584c724478ff080000000000000000000000000000000000000000c484a123f0ee8e4cbc9a7a10acb26d5b348b3c3d0d76b90b87858031e1b4a849c1cb836736f29d6e0dcaf5148e587a33efc39c02b950a91ec2a0697b0beacc3eaad67675e135e917661c7f026841044d0bff800326f974680b63150ab8fb734bbd015f669c1ea610de6863600b758b70a1bb000483538e31fe9c311782d51a3fc297de781272935ba389641cbd13d711eac28b4f44ead93c8fdbe96dd600ec00d70d5015b48d1033e7e7bf06799d8a1878d6973dee60fd37530c3f207454510eb5ba742c5eed733042561327b8e1c658e0df077e65cec8702464bf24000000000000000000000000000000000000000001533b56c18268277ec28d40380be725dd569a6a79b8254ac37a80636303131615c58e31388fad714a788d7dd7ba1423e353ff7d859f93261b52413efb2d4520074c6b4ec5ec3330c797e12eb0e5a2189836052023457d2962a2773db7d4061a2d74544773cb5c0f487d4c0ea547711d21d9760a7a532f4c1ffae94845af1930264a3f6647f3cd2f622e7519f59adf6864d7af7722501935f552dc02bc7dee2d504a102022accc29a0a8bc33b679672259510e4c69102f6f46c60715a4eca9314198a4241547fe1227a679149c13c1443e4ac266421aa03eefa8a8390000000000000000000000000000000000000000d88df318323def5d875200184660e8443101c43c4eaddc43efff8e56850b7a4aab12e60a153e0d23d8bb2c0104d48a4eed16350568ad4239aea1904eed43d972f3d4335186fec615d6ffef7d5550770e2d27667e474291037ef9a12b025ae67289f97b4fcd5f730a61feeb032444310bd01d515764616916d9684f65c0fee6710fedcc353f70b0652ee57e0d77157739094e865a12d6d122b303b4620d7cc62abc95f206b5823e2c53af4a71fde103526a681523e8cbbe23c6bd4b07a1a3407dfd917b2e2fc1952353beb225c5d61e4a0ab02a04691cf3134a7e120f0000000000000000000000000000000000000000d471d52640eebe47d081f97e59e9ce30cabe010c40bbc567ffb0905a45018539ba9bef513b67236c0000000000000000000000000000000000000000feffff0100000000000000000000000000000000442d9b5e3e9f4f2314837e391285be0305262578e1bb46183a1147658c914a39d7cf350d541c335921d3c82667219c02da8fee700a630b633ed9a25f83226346ba94a752675c975e958a7a5fdde7e47b85df640e633a3a03615a092e4e6c3a2e0efeaa189a3b8f7445e4d67a7308057a5caf03668be68706052406113577ec516ecbcc035ef24959d14fa94baf0e652e487928308405d21120b4ac1e4e639b63000000000000000000000000000000000000000093cd3c0ac90b091d51cb4d0d9e9d8147c15a9b60de7856582ec96867ed6a8a2128255638220adc6adea17726759fb17765606f7b79ffd8633d9cc831ad671a3edebdef07bf78cd0b945fe327e3ebc5048e474f563ba43d0257d9b97cc04bce0b2d7a1d179052816c7c3d6f39e3d7163f8dcf340053c7812b75fcea7e06e8833559b86f3f1970a351643e031f2634654e3160fb4cfa3cc206b1c1e657c7f9ab7b000000000000000000000000000000000000000012742d345917ae61965743362ddb0a1c077056222ec2031a8ce56662ac2cbb43f8ea3f4974399a756fd469057d9cb94390ae925a2aaa1a405507c045c6fc7b4cbfc3ee5543185f25f1ad8d1a72b0585aaadd9b521bc5e34c51fc3f023306d53b6017ce0ae9a79c22359660000289a81aa47d9c10a593d94553404f6589dddb48e01e64395b247a64953a241680063a0a115ffa2dc00a400b2b282c2908e8864d8a292f30cf711a4e56387872366dfb18243f2b0fb379bf5d97d8fa6c9229ab35efaf843c22e00d02a812eb69e6a263052e9f144fc04849595083ef2fe24c147105a9dc195ea6c03bc2df3d592b886c6afbbc156e4408a330cffe155763268e0d6764c703958ebb362f43a936885ad8181227ea2d6e53a962dc27c2083c9fe91b5b821f728014a04b1a336c0073be8849fd4ba258c1c4e1478418c30dfa241c3e8c5aac382ea45153bf3b1149980a4b780a8587396cad5b047612472456a9cc17cb192528b57b655fd542911e3e04e009c424886c2d00b509a3d09c1cebf54d6567530f5fb5b7735f2496a14be88dfd4359d914285401555711e5fd5a1c9d7821b06c036edc72bf395a54a454968dfb5df987cb66d0d1516d4abdb65f0e792d648c61223b82cf4b5f261afb56ff09312cfbd56f029514815df20c5200c452ce6aa5a53e6c50c7e4315df761255500767ba5b54637238690092003b11c60ee827c90eb2d37513b4c7506ed10181606421031d85743d9f84d6d47685b3ad16d53026ce05d6be3badf1804b4e26840abf71a53851c4e7cdebd3aa3300f00b356947d58ce3f168f4b115eb4fa0a5c25cd001198f8420e70ca7e77f6627a220a65005d9697bc4d0cc7ee15d2ad4504d648e82d42cbd721486e0c7d6cbeb1421fa7823bf998855aca78eb663857ef67579df50d52188c0311fee0539f28d11d83eca724501ba37b505ba03f2c59d47828a92a242a1ec32705cb18235e4b1f6c72402e29ac3a307bb1aae73d74952011668fa838f0dddd56a4b7162cf1458626bca0d609ad508a3e1af3c4796f78f7251835f1084dc29767a55b5624085bd218ddc493279e27af7a642fdc3f9eaade79a1e93f608d0034238c71c12a50f6010f6ed9b22bf3c7be1f7f305e249835b54934741a3deaedae23a2244426a522ea687846a92848c87c02b082ba538a8d440db60bf16296ab9079939d491b7a97242e81f12a3d15eae2322341142666a6eb74488a25490c446024ba02157172d29125d420b172df889124020c380c2c07014834c3263dedd5633738879c496f39dd397c04e543fae259425a9c7b62ac861100c91e133f5416526e35345c3069e3b00bd38e7f595c45b0509f9e314e6cd07b0cf9a6cb3614332768aaa7e21b4cbe2a6365267f41644f7a5cd1f7e72dbc88a332242cb64aba10706e0b6bb2291bf03804b27eb034528f587b9a30506d1da8ab185cdb3865cf6523315fab6f6e1b42c60df811405f0ef1400fbd55676f1d079279e3038a2c99cff76a9057b75a6b93c91b3333f735ab5b6c121238da7155dfc2328a54560fbd488d12ac60e04ac66e925d731a5124de3fc44a5545652c1d709069480f004865f8382163072c69b022d63912493c365b05ee441a8db22f64a2220260c27a104d075d6dbb6ca42720d89e2ced777d484b380e3e6e04f156c33e661bd4409e7477f9aa5529d88c3eedebe5233377796608e613728d68c079d7fa5f719c6728133154412c6ccd6f70a3abf46a552a1d0ac5a1e9703b6b6e4f82b16e3afc18a013038b61549a06c1531e5e8b12e9e88f5104a7776ebcecf9353c921a12fd5f39633cf7c545746b6a112e8dd44434a4b14ae3d3822b70449317270fb71c1ab2dc5aa626f8535ad3712278a6572a75bce7072f54ec3ddc9c905866b21668c562375cceb1290f99384627a4cdbe2f5052d94c19aca95750f2b1697e0bda50c5572166071c1b47c4c19535d9c588489665ef25dbf08f217497cc3874a0a76913cce6685df600647d09ca73b751ef0dca4b4a72a1bb082309d92e667dd4a65bba318a668581db1736804b23005c314faea7202870fd4c0e8b1346532f81ad0cf89fc60e2b9915134f02eb724e69c0777ec745170b70c5141fb30668d3b3484e6d1b341100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000674cb7746e112c114169e677cf39b450f4232e29545e1f5cd9f814633f2a694e63f5761d37c465083e68877b0b26445b4184792247b40803316d9c0cf64bab051cf9ba3323106d592c462c1cdff00e04beb9915d56a8337b67dd6022f935ad617c17c852358ca3755e3f953af188084b61b22451dad65701358ca3755e3f953af188084b61b22451dad65701358ca3755e3f953af188084b61b22451dad657017582164aa48ca03b972a712f0068456fd39b7e6d52c2e9269145e365a61a57713228436ce784aa2b00f3176af4be9775825d8e5c4683b633812ff968cf1e443160f2f53c8dc32d4afb018c70dda87c7e573e3b7b108ddc20e3e97f00482765403d21f6252a26215a20ddf7078900c425f54c6149db99534b8494707214e7d675c4124a24c16e5058a14d821b0c462837633935227b6f301a9b27464c5957515ce43fac6f904c0d7cdbe18477bd5c0b5de7fafa53ea69526c92c8252f6afd941bfb09900bd33dd9290ae049518af3a149705cfe39ae3c1f710eb7b3153aa6e445e1acc57389fc59694775550cf66849210e748116fe7aa309944b6f368ec1f37935cfdf09d883d102c126d20b6379140ef8f23d6dd5b4da7ca57f2864296a292e04c8f702bbc3d14c8423cd1d8e717168482fda13af5a8b014d5fa92c76f03a39a0868b47bef8be1143e36a2e719ed83e55d6893e0b86730baacba7739bf8a9640828f37e96f5b94a23761945a4ed25259f8a33622dcbca6a41ead623d38afe39862cf86b1283c02df6ddaf6aef670e21182cf9469d0e6b5c51a0bb31ff02593275d8c74beeca92180c74d833b9b0d73736bdc265ce14d34bc814b5427556c43d274cd62f6535596de67b447b23ba234791f0370bf9f00040b660397b35b451132c51197a211919600d297f7e3b95b44c9f02054f27f8966228932d693e33ed775676f62593f5643a0000000000000000000000000000000000000000feffff01000000000000000000000000000000008249b02d48ac464a4a5c6c77910bab7231f8690adad27a064a750f51aaa4657364b54f5d240d92077b7067330202a5194615935482ed9c63fc5db618ce7a77003a328b6c0d9a7665401fee7966cc356a1390494fa574862b5e78b024b86f67585051f56c604642197237ab0c8620d26d290b6f1aec234700976e67183aa7d971dcd9761a5ea1e3217eb9ab3eb0033a2d3c8fcf0790d2dc1cf6d78b4d8fd0c4490000000000000000000000000000000000000000dfa60226bd145b6ee4d84c24a9268631999ad52783ca5402711c1556c561d8043ddbd92dc5d3e46d23271140ba55976e4913fe366120fe51a380c10d0dcccd3f17a4f41a07946a21693adc77b848ea2bd1380d096549ea6cd739823aa06d7d6cd9bbf96800ab5241952d2f597cee020b0a537a08d72c29147b3aed405ec5377798697744317c620a79526450f91d57601ab0f604a306c81aadf6302a6e8e3c720000000000000000000000000000000000000000c7050b0b4e390f29ff4ea90347bf9618a3257a785490ac23f4ca504e98676a5b226bc25d56e58f35102414032e26402523504e1b1535d2535460276fbfade02fba3c487b1f3ffd14354c53205edbcb5ffd950d3a1ae0b65d5c8df725cc98c250893edd7dd658e557c3b43a44a87ecc0ad9278767a855570b85a161162f4d13231a84bf60a875ed731974e00a0aa5de335a3d29273a5eaa3ca68e6503befb0e1b4651572b86b3d53145c9024380699965872c9063caf1fa4353a444405cdb394b2183542541a9884594f78b0c1924827bb5adc73c2151d875469f1057625f053bd4826b57dd9aa4630e5cc060b766e72d28255567872d504cf53ad420bfcb381ee66f6b37bbe9a330eba5e45ed9d6106ee427664b7fbead37c3eff660fbd0854336dab113407df71cce64800e5513452310447d61936c8a1b574a93578780c65b1c7018184279d749698f26462eaf635180f8a8283914be5c20f3222c2987b454f38412438aa93e088fca496648db1f2248b2093c37fcfa16934b7625ce3cc919314fcc5176102b5fc067bb4908cd26040808380b30e7862a92173c4a1dd987020d48b36edf691d636b6a2e2c3a7cf770d705313be6bea556ba624d0d3725054a1bcaa756b972bc636822d84e36c723386b445e19d837884fab237c4b1dc16416f25bce332d5eb55fa76894096345850cb1694d5b13d3344a2bb5a54ef695f30ae565855b4356877e7626c1495be1810c85521710044c5719482fd40ed49f396f78d87f0c58f6f54acd6477410a6ad53e992df46170c6bc70694e260e5a617a0081df005cc14fd440683eba04b18ca44645d258096b95a7661fc0ea2d4ed30858362c0275e2b1b43da2202020efd65a4dc00d57616078a166fabace21100f792948dd957e190e7e17e25f283a529eda08cbffda6e3d30cd59e4e907024ca78e4198e8a32b136e8915a223af497b6fe05465335a2997869141c955cc316d31971525fb9c536df67530828d3434d48fe5136b8cd91942670653ddce8d44e6093e1258502f10c76c3c4a21e34911f94e8f419193053d18123565a353077d7eb4fa71b75cf234fb807875cfe7df0ba7157a7d4506266189e6d70ffb50756cfabd897bdc26880ff2454770e7e51a3ce95b1047537f1271b61cdf28eef3404c4b5cad51c1e0ad423f1bad747c88a3522427da5cab01b61b24c66d4d05aa2371b19241452558830789a5b33beeedd056d16b622295cfee7daebb026d1ed6fb52fcc7b4662c448549a6803d0e6440a6111056bb40e05acd76a3191b65b7091471ae35cf324416d873ee852959e99f523e023e9c197afa5e16cc425d7842c0677b399d2a4245b500337742e56a938a790c37992d6082148923c36ed6241e872806c7ba5b5e2d474379866a78484621dc22691d704ba5073a6210ca0b65487c6f2e0d270e0074c0941a31803f4376517e3ff691fc75ff5ecf5466181c7d1596d740b8678d1c80bfc30331b13412d757ac7d59dd1b2337571f631dabb2723b5b5f7c5a3c0403449eec3f6d0e9f0aa401e5039d89c94e24d6932135e24d6324f6c575bf10d96db90db6794f325c63bcffed6caf85a63506483b5428c4ad51bba2a4716904012c16fa11224e20625a5dec880bb4a4427ae133333f75100d616965dd65cd9a1b1545932a0e35ac4662d861c76ba95bae29c0117b7e6ebe6d6670469d58ec4432443052a615fe4e304405fdb43d2a8afc4d89def236c7d7c711d9f9503bb373757110b1c73925b2da207f48c83d5693942633f3ad0da970c503fdac061a5acf800243657552db501346972b5f49ba3f044ff8b92a0a3cb2a6794dd7f577be5e2046ed3d575052c8b35d0b560300a54ecc29f827f117535cfc56282788799ca010341924125c004f8001737629449c925c4646d2764994aba33f43663416a8b73f25927721745fc91d1d6ad7a21b794ac5776d95a1351aa9410e6cdaca745ac7bc385ca69c531d329122e4a8bc24ea62d00896d4e31bb875066d06f30d445be92b4417a87e6bcb54534101c7aa6c83838060fc19a32fa7128d05164fbf61979b310792e5941cf14610187e00e2342d796a1351402e15f09dc5301a84290a952a78048103f36cfb69946c4de60119802193450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065920b3d46800867c827e50fcc5df44505672f4c9f0ce077e310ba51dec8576b78226b5f33b0d47cf27eba1a7a9bae7b0108354274e6877b575b357caa2d7613eda06e331474c164f63d880f36af90303b6512363c36b03a2afcbb5b72c75f3eda599f596579467a5cb14c033e11b71fe58c7f14e773ab3fd838f20476620e46bd72c5668123e80bcf613465a5c2247c1915df28b420f6271eee575dcea85702e02b627ca8779745548e8646a4286252ce5e892ff5ec5803aeb0ca041f91a42afe1f6a474761533a7eabab667b61aa3cf85b5d312d84377baa896a514bc8a11f87ac325ebaf9f16871f5247678d3704b22c6271d2d03cc45843e552b71116b14ecc291791dcf5774e4917002bc2f0711b0b7ec483ff484612b2fad1aaca0ea482aff7301f80e692eb6ec2e76880e6502cdc5bd7bb3f2c90fe22e8341d8f39f7030bf1b60d682351ab607765f6bbc8456d05148134167191595847b0576d07b2cabd36848da47110e5697be5ebfe61b74362c9c7eaf4ec907c980c263ae42c2681de5da47ae1b9c2a2ec1be0facfe3a734ee3da2e8c631d53d11aa8601f616e159b8d942922b95718d7cdac43842c295e83affe69cd25662c59460810f392e22c5e0e354929f2b463126e5c7aeddad128a0a9e004eb1ac52f40db8528e6dc8b3aaba05375ceba2513644f924637f8cc619ba2b949ad2dc5638b502263b9cd220b5da7e55d5115c872ad82e46825cc5f5f06eb326ff2062c29ebf96b7db0db102cf29a576c63dba3280d920060247a385e76b8a8239f98f84f9174f05e6a34ff519f628e4d673819147b3959197097b933ac0191753846ea680b3da83d7592000994475d7e12f3047225ef984512f5b55c42268f1a61df040c2bebaf7a5ca88a6b06080c35055df6110e42ec328e914e643235534243cdd011f57ae512019f2550d503501674d9eb2ef9026c35c3ee6309fb1df34a28501c27f104e954c1650f21801b8d089009f252f49f7104fa7b9c5670f260550cecf2710b96f44a3bdbdb23824c934ef91205793923974a347e07440f5f545b4226a42b84c8780ab390050a286b907cce4a06117c0d4e732f4103595dc4e34767fccd7a47b36a1b096ccd67020d6b768737ab1a3cf684655ce0a77162d770635f14663950ed6e06b80a9d279530a352decf1a166c8355773b81bd31df97a126a0e1990f985a0f4cfeed6a2a3786de6b17e0ad4af8b215642108e0322f100578ca3332159b6ab52299e623758b7f1e6f53580178e343410f50b5f734b9f8f107e5136f16bad21960291f0060b28e0d00a9dfa514505ee5121b436220fa25495ba7c0dc26bd805802e83ca41f6c402d64429f914d33d5ea393fb4994b57814a7cebb6e93dda7f7473068f184e61f9c30dca27947338053f3730bfd6309a11e25257bf7944ccf091277d552b066e803c437fedda12c0c2660a9077e73a034c3e28520818347266526987139523dfe6dc09b7e2f9634a7e7f092b8d1306e80d454ddfe7505ba0d0493d8214de054f6af83bdee8af6d4a738a4002e3bc5eadfef407b828d9122b47fa24af490162b9dfc24916f0334f34e1972311c9c115c52a146ef654b238f59e0124510f301c120d6810311651446c86d86c0cf9dd6c4dd33848be0bc37d1e85be0155f9644b88b050452e3ae67b07104a2709c11b20ef37707263bad43415222a73c57f1256fb012864a5fe084aa6bf684f856bed435d436f10cf700b39279cbe2af612ca73ed101b22bd56020ce2e12059ab3b6c25db0a6c491fdbf61087fab13dfc7e6c320d2d8c1d15ea4121676b491e2c47796237d62838a215dd1cc685bf3c5ece201cbecf0172beb27a7eb52be3661ce7842b7c725c436f9ecc49939fc5169eef900f5bc9c54f0dac3b6689b4372c184d4a53eeffb920019dc94e6d9ed741e0eb8e325261920068f7ae7e5aeebf0c40885770daaf783d40004d4fecb30d5faafd5f580ca6e7774c92fd079e312c3484848c3c889ec55b9c5f47580b61930bb96ca31be394a52102204070faab3822910be714c814b3295997c87e1ba56c255fb11a468b3537324d019958bbda27423626ab2c3f220568dbb9645bae488c4ea48ad65c5e0dcb312b48ac6dfced65082cda673575850a16c0e85c5d1f73f64ab297d25597a7182d9ef3d25a8a63353dd72c9b51004a03207f64f65ccef74908559b2214366d2378dcdaf8236d19b062aa8b205631540a3ccb437f5a0ee86e50556af113968a0c2f65e09e14f87cc876113f5d527d0ebb37dfc84115ac6a7f27320da8375987b022a00e565e6eb73f2b541dc356264b976d68d721394b698a3449601c4f75d6e875dfe881429db9d43797395425c1df953389290663888d0a02bf65045b86ca1d7dbd3b9a23fbcfbf02e5a4283bea578b65aaf1bd4640d34d233e90b113500e3879226f30394855ef43e6d44909bdaec962b655580f18c633148ae73677640b311045e2760765e1d05478420c0a183f5c4cf243e85d51965957084a3c10b5dcda29b71cbb66b3fb883544cb8879b04020036502ea630cba013e5f81c04ff10cd33097c36a67c59e77315e631a7ef2d12f6514db606b0ecdf42bad678e29781df63c0a648c1dfba7a7611c1b8d3630091c6c54e9de318d079621bbfd5b4fd43d0f4d8e7cc93da5b58a62ef3de144974bf84b1d376a10fd3dea1f9f6f1560c10ccd14a5bced1b3e4427603c51c82fe9e1217eecea9e3c681781534c07e04a18d2746b1babe24e14427d18fac03976ca8b4f379b97057c5a221732e9fba875f52daa65dfb1295002f414308d57a06ffd6267621bdc9e2777654451f074612feb0bbb4a4efdf47a9008f4440621ec356ae00730b78d4d3bcf89d26329649954420d5406e4b48a17b027272457b9054e02e460764084032f9a818b662fbd7059ea9cd23697324330d54fa2204cbb30523e8cc75696329c00df168507a6541162aa067a2972b26536e95938151ae6fe4ab9d76202de56a915cf5b890227a39f5c801f0d6009a00e0689b3c35f604e070017289f295f08764e4042f552c95c425003724721a7487f690d1f7f3f188ef04b258d3b012f84736b08d2543c5cc2f2620c002e0dd5a9a3668248b510550c8c1e1f0b947922d75e4d0b471d4ad25a1d4445392926f06ba72c282cda3b2c1c183fc5db9a4ca6ce8526d0a4b23f87636633bb63f368df30630a9834516ff6ada05231653f4d14cabe3e432b1c2d55903403cbb26358537725583f33d6670362b2054dccbc644914185bfe27b719a93a861f4e510f7d4e52357e8a310572612187448372157bc0b1d842fd6513028ff026093ed7ba1df25f866693916d64a2ddf61fcae34e3785fdd254b646ed4d3aaa1602ca1d2e2d46138b39119a5c48a433e5336409884eee5d4535aef81055738c7d371bb43d0e48d2d164ae22906d867c6519149b317941a7702726882e682048ca5e161be85cb110c225701efe71b74aca29a0727a2e7672ba6f2ca8fb63c4afe23fa3fe9d36686b497862b83e68738a5d044bb0cc559f6c224e3896612e8d9bd35c4644557ed883e428bcc5155b01e4c61ac1db4a2c4c0532532b1323482b2c883cf1e3c62ac1db4a2c4c0532532b1323482b2c883ce1e3c63ac1db4a2c4c0532532b1323482b2c883c02fc250237621554c8b2bf6b551e726bf6d39951f2fb251237621554c8b2bf6b551e726bf6d399517d5f3f6dfb1a4f2d18f71361d05bba7eaa2ff60e8b6f6a1d187f685b47d5b329fd38a8647848013fbcfacc4317ec20749907080a46c82a66f1f61f2271bbb73752298f57684e0d2286f8b5002f58fc5ee906c342df254015b193d57265c7c33623ad6a7ea2377c181b19d7711a5bf55b9dfc6f3fa3f1d41bf65aed738736744ccd3c0e7249bf8642a4e3875f11ebff0555658d36b9b9c73baeb9146e7bb8744544babe00fd5cdf6429c309251699816892beaf627c746043aeb15f3fb2e8956c84bf544c05f5995f8b5a9a3585f15a191d67e71036c01d45f791eb5c686e626111c0c76b0f75032e71bfd06cd79cae4d1a43836e04d3630c3d6fc02bacdb41415d765663aa17ee423042667cdb00897c7799d6572f059062b69f1072d51d9310778ed40ed656be18e527d0482f4931394373c67da4c1cb7a609bf935b2f01a3775636758c5a9ed67379e2307c9916f52f968c203e6723f52f191034ea431dd1643f46d7e5eab4a70c24e64155bbfc601d2a418477a3f1c31bf820868d8ec4304590f314c718b653b2b9ba51ec05cdc24c2eb24230d9436614834280fcdbf825639c97b5b64fbe97666140c0af93e8b545ac947392c7ac46696837a40d778ed7b537886412d368e6e0e972445aaee687b9b65e65d3a26595044d2f1506590ea41a130ec3da0a26e46751d135e20b9223d8322166106259b74acb37511fd005671bb0a1a645e724824153ed049bcf1a3240cacc5093bbdc576b793fc4e1de07e218ebf9c2360ead5229980de3b538a506fba7cb800a7baaa2a2d6b4f643b00f16a750981437830e52b6156971529bfdd3f4f4b786282a01c026290f122e2c7bd5a28346c4244839d24984bff229827a85373cfcc6040b8fd77cc6a5416f2cbf1207929a32d1a8af016581e83320d069d329de3c51996f25956076ddd7b614ca54ff18174063f69061341572a1f8e75f37ecd688c50b23374343d25d7681f147e13b435bf6d3a8fc065095b2073cd9c6055d1c04c3661f0df1289b46d4a86c6781bc9a03c2af67eca358a4934035e69ab2f1f9ec96308ef5f5edc00800e7bae6a66f99fb83e7daf0e79f0cc723a7694a351377e3f635ecd964c8b4dfc77affb962a6fb259639a63ec6ab7375b51fa7ad323c3fd536d22bac32402a6c3568d6a293cc1152d2bf23c8d39c5b3e725a5802a44362a8004e5647d0adefddd4ca000356b2d9a9b4cd3132e0576631849eefbd604f631127e79125e3fb8418d462b0d43291f4b744b7b4de75a6ecfad2032bc3a700fe402083495e810c1ce3c353b24ae653e98e960d0cda92e3fac17057ada524b9c2e3a114effe020ba3ac501e0ed730771a4092fcf55fb3224256e61db17ac03ba2b887a677f5e1cceba4b414cd01516c79cd7508537271b3c12ec7904950d51bec2b6519e4e474c8d47c62080122c336c5bd0488cd48105d72df7625544b867f92ab955b9d9805a9a9e701f055d5d52cac47103a201c532505ca03578e9694a98b4de2f2f1363090b3c4f66ef19fa190dfb2737825242487aa4e666c755a85bc0df1e292c759b3338f6246d25e22a17381e8d78d2b18556f87ba26eca69e035cb5abe44306c016f6f06f770c8f28218d8445a272e17d52cf3dac5095c53726e636d303712a8c95fb149e26c6f4749210f85db25762f062b53a4971a2bf7cc0d41d21a6190eb45081fc8c94d28b7d27295e81b1618df86141bf05f61cc313065f05d365b266b2571929ad67b9659522e65acc05185b615455725580fad1ca052578f204f3295bf7a7ee0085e2d2872608ff64e0a472dd47c3f16770c8cc5477367f91a6775a3513e5669b3421e044a197e5c1265497be329e1546a1ed4b19928f0d89543b8b7af4b36b0d103484b972e436cd718ad0e1b5b8ea35e3fe86b0b677d458f10953bff6532b9360f37d16b2c761a59031ab4b20ec511cd3386e0317d21071c07be4f51067ff819406695414c114d93410e5ada7cdf097a224d7e501e80fe6a31663ac678baac5220847aab080da4af311a8a9769cc6e9e64da37fc268f87e472a2792864ff8c5f5c5c5a1277ecbc5e4d2fa72d5d338cda3967d1d41ea5f32c77126274709214aa16ba49901ef9134743ae81e2208595c1366f6c3a6ff0c5f15233decd4abaf77f688aa80e4d49358f2b30ffa1752b78d434a5dc9f211a2fdd2df0decd40f6a30b1684ee342317ccde5c59f30959b463e154242f22187178e9568583f735550bb8349c9e911dd43a0e129164c97275989b65a002ee53ceb2941cf06d85753b6424515f9b7019ade5171d750ea7498b7f7b3fab4743708a29d2702ab205531ab01017268a32047a189410f9b793332bf541228355e87e73c2d9708d438a5bea157700da55834254d4985b28aefa66c75d435b85b5632be283255d7b774856f7ffff07a314e93eb976cb6d103b081e2746d540e283170faa9fb1408963a4011fbd6f0c049e614483a78468914fcf6ec2cb3e3e9786571eff22cf00bebfd2651bb05f7ea1510a54fdf73325dc63390a04876e266bfd2e17a82beb5eb95bde4cbbd992665792bd57e4de5a2c98ed317e6b9a583a403712243e244e2e48c0573fb495955664413e3d96ae32244da5b11ab2f2de5bd90f1d6297efd83b67761b740ddd9e79b426e837740ac570aa4ab16260d53b3bdd7a72263d2629208401aa4797b2665dcd741a06308b563df6d9d7291933061e88a55805f27c4f216e45ea56812d5c39d04bcb4c3d24074cc050a0400f6a854fbecb525f59f86630bbbc425d3d35f8160c31a447798b351da7049e58f576696c6eae12341ad3707833e3dc68bd676f544c7f4d24e1e8fa4b691b5c2e710e557dbbf0ae4e3df14c4f83f9aa2cea0c9449508c08070679596ae026b5599181b23d84824855bac16649e54ee90ad855f746b22cbc405dfbf04a4a5e8119acd01c723d02b94e21c55f2f1aea574fa9eb5a7176c5646ee60426659ecae861c6c1910ab1a3514c1691ca61b0fb4507df3ea826ee8ce16fb27c9c0c36d9c60659d29571409b785c23c3bc5c4fa0ec0b7fad5b064ccf950cf9843a502f21b35f810e9248368aee27122611413ff6c44fb9ea5d559179285bbbc32177d6833e0da5b0071abe92b929e47184548dd74602eb072a457df54b25b4bf66041282251530fc1d245d3d35398021e92bb009e276289e2041b30463485ee5cb7c0dbd6571d8e492066c4cbb263d618446aa69e3719dbc5f6c16668210c5b3ba760320131b5d3b8e29dce41153bc095152a91d606124450f4969cf0331ff7d1a431362374ab493b2173ea6e1038aae9b15ee6203021a39910cb085a54b302ead695c44af0acc0b1309b96262712d6d5765a1a73a75d9934b6adf36a265b6b0b570644478336fb2e956c0a87d7ab826d30a96c11d6debb8191d75829413ccfb2d6717813567707bb23d0358740d1a41d268f7022b36e0b9e653aa590678d820f45563a40b75f72bfc734c8b6f47c7828e02ab8d1746bfaaf11ad8536a33966034090b8d62156e8aa524c7a08f61eb8fdf036976bd227af5f400fb7ab478cb666255cc35ba059a4bd65f657a9e2d4363f95bf9ad7f30d770025c0477644c9522093ec275752f54d4e10e7dbb9a793291ce0ca998746f2438e126fc50757b3f2da20323a6167b797d2c77ded3d5799f86a04124229c27ccd6492f2316130aaf17801e1e51fa133f4ac0074ac27e4e64769662e4af4033e6e60a359961db7e5340761624c38232a966441b959a451a74571b35bdb22c3636786f3879df9f5985306d08ef21704bf328562fa403740135421e4324188927e0440517d8b8237d3974ba24fefe57518670a0717f9cf02fc469f07dea3d245e33e7e451aeb6326a7f476e0b85f2827a1989e349159fda6c89a6ed6b107b077872986508b186d30958218422b4e2b25d6132ef2884a5d76c819bf07c5e99c700714ebc75831ed0646e317a3811b92a46e1a16a66aaefc95d9003127b7cc2272fa2606e4b06010b3a3a5ad555bebd5a79d3a6985d11ed523d7055ba35013dde6589111f56a9e4c84c25a3fc106ffb761470a7fa667f41871d1deaf133285191316921fb533aefce543b644f4bd26b363f29015d79c5afb9372244c014c2e3ad76113e8b56edad1b4244ea9352aea7760b8115565577e143655b847829ee89d37ca05de67e8c9dda088e343c2bbac85526bd95eb3cf3ac636418b68a1e54ea4251bac85526bd95eb3cf3ac636418b68a1e54ea4251bac85526bd95eb3cf3ac636418b68a1e54ea42518fd5fb08c697700b08db66338ed60c43c763a76f0f88d04b26da697eb07ab85536b399489ba53f2e1275513e209eac51ddb2df073f4113507a40f2597ceefa1a1959a6781d346e7468e951660049525e59b54630eb0e434d20ff860675fb2077e0403618f6773f0fa1eb120c3811163068b4982a545dcd3d631a0a64f6a37e2ae8b9e70d3ef3f455b03e646869daa73ff260771bf1cbb22070f1ea2b3ddafa66791f6d28dd44143ebc07662ad5cb1713e3d42a6b2268560d8c75600589c7b12df05ebb2350c28340f6c68230665cb57630cc7a58e6addd5f73ef436197c8182b8c42312db08c386b1cba771e1930ef1ef805f6410506970a4ca30142a70d8d29cc64517c189f930e0258c859821d5238ab8636405e3ed301512e825a3436452653d1a526cacf3e65b5defc3283bbff0b9b26fe185c02e5356d39841c8f38ca1c9ea111411fc0c1009e1c21687e2cfc59468f931f79ab804aa2b48f536014c40e575fd0458082797611c936625af5713a8fc9b057a7d585594c90f2622ee1c04d2f9ff774b6a1bf5bc604286cb09a22669ce67c1f6f528f2fcef09c116fa790232f054a73fd0c4a3ff0f27603da90241e0b89c81f6b5c54344b2f446b0db30d2c2334ba5868d973027ac6cf7a9ff4011dffbfdb7226a7601d4f3ac053f42a9814c7a29b62b2cab62e5e5d2c24579b233edd0ebf397c6a30139f612c6686b007465be5852cbeb4ac186c6a30239f612c6686b007465be5852cbeb4ac185c6a30339f612c6686b007465be5852cbeb4ac187a518f469aeb516543cdd948811b25773717bd196a518f569aeb516543cdd948811b25773717bd19de7d497cb496be561ea39f10e9d05e610005ae3c4d70fe64012a212fd4a62b0b7299b045f5a72222b6bb525e7731ee76fc01915497d8e231a46f024ff4ee2958ea973b10edb452602620d44a22b87e088a413170a730af337ab8f43a599798590837e9706236b762c94a4a751638933a4faef475437e30189694a2038fd09a2d6b7a50646f96671782820d272659510d6403174dc761075be065bf748aed52695753b30855352c6797ce347ab7f77309beaaaf783ea9d824b8200413db4fa623b8649554cd5597730c4f4545721b402c8205312aa3f4b46418f0a65b9fb1a11ff342b345718b5d03c732995cd8566d083e102c19d98b355ad56fae3e34ea9267c562d7094c09fe43361d1f0b7fac9c6d45f7c83d8563e7361e77dd3c3e029764875d2c66c55df35d202c1c4771b35d6045d12d73d0066d4607625b21bb6cd80062e4e61abbb66e1342879875e83b290ad5b8d728aef7ab1b55309d58d31c56506d1b262184a83f2fe22a224a6981da1b008ec12ed5307a563f3c9a66491dbf7749b3cb715559036248fc996a3461d2127e23962315a88b453deb910b9640fa485a8966363679e6267282fe748b14d03b7d3b9765ff59b3290af9e95a305cc7479de460046ed13c1af9c441790716c0256fd3e95bbf0f4f041e829b657d0a9421b7b2b359edbf3a79e72f3b308517c77af4e1751b2169f7219435a84699b91f5976c186235b70b607223bf12f3d082664e48c024caccd3618fc150604deff2a34c41a696035cdb11b76aa930785f40032204bb21d15130845db22dc5d5bb68039a209ff462599bc34c4d57904b5134f6b6231d34076f17745880aa3447381e8733612f506b852c6541710184b6dc114676fde8a45b8bdc81efa98752f063621360b75a6630abcb97e5c0983775ac3541875c6967c6e11f1157948ce0a0d46a42d2b204a6239650019b1021360af278823dbcb6f66d7138b3be6bc746081dd290901663f1ba1017c54d941de14e34f4e3c841f9c3ea9fa06182e4cf06507f7655c51fdf520ed9bbc529e4e7a78e04f266f998f6c1d34e79b1f92a1a908df19284279ad04299f8c085d25d2a6609b00dc65817b843e5cd0a911d65cc737b368493ecbc74c76322a414bc3ceb63bde59b94bd4e3d54816190f2eec4270409a5d07763f95df30d955e260a35824322599800c2bc6a56958df96332307167d96b3ed29cf1c140c3c3c2e1271dd9104ef76782553c5d53a10bc0211e7cd65091210e16bca7740472604225859d7903e44f7d655ad116476a225ef7d96431f4e1dc65255d7b92c22ffb48104eacb270f26654c29e177045b3d5209625e819a37e48eea1eab6cba0ed875084cac7b2b2198aca45c6ad0e34b2aea2327704c614983b264239c1139188c10763e323d42781042ac66eccb496396ee9808d3929d0f2b2d212ec9d171400bb2352d7deae26d09f78220173e6d19ee25c3472889f872f253680959f79f25d5e663387e245e68a7da947eb251dc0f987d2b0aed054e2867055d60de2f9d2a48afa133d20f927ecc4a4237f2966115d890ce42b57d66592c748b5702c84e0c44da1f7197f4931abd482b59b8c3116380898a7d0c65e026a767a13b64e25c37afa8867e506b467520abca3ebbd47247a7c5276b9ffd075db956b0742dcc1370cfe9ed15e6782775ff9f4f5be903b372a9febf04d35a0152399ebe7ce010fa03b573b62930a1fe31a50db24b1027055d4a3de64a24acdb7eda1e71659cd697695776f96e9792b034c3069a12df51d853e070fa7c64bb0a1a1452b11606d4d709799a853d5fddfd533583656fd1e15807dbb9d2333813ca186ef0c448f6315529f74f3b7d713ccf29b56b0217fdb8534596615d23c5fdce61773cea764fbb1349d41a1165bbd8134a459c212f2dd32c7c94743307a60d7c51137973661e536805dfa13c6e7305c36108d75c5a737a3e171dd07759020ae10a3df97528987a1d6fd3d2cc754e8aa20642c3b11fa65abc7e4983a71e8f32000f1fe0543656fcb6709b348b189bbf1c6f77738322646b814a520c307130633c4d824630361217c8188f4ae44bcc291c52aa916a3785d125426cbee032a89e211c566aa64d9df1053664b3e4118e49c42030c51c44486bae0a1019136a36672e3cac6b995203e8714eae07741bf7b32656e6f3a847edb1b661496f9c75393a424a97675d3261723804a5db0c3079ddee1fa9e1067192bb224155689b524c5e012f79f9f90b355db0003e769f7be1bb8b7aa0211e34a3af0a2287b9e32f4fd2d9079b474e788e491176b42d491c70bc96013bde6809970b9529ddbd6f59f5bab82be568a213f650d96555da3f0f282d3b75eda59364045f753653aa563bb431fe615eac1023dfa166455534201aebda5e60d34654197ea83d343f39cc4a8d4da749005ea944b2c5a07ce456982c6ca8ba0075c5ca651dc37f48eca7b250285f816659f7877dc97867431b50a179b72ea82937c94b25ec94e33e2944de3d8c78fd1602630f20b06f5b597d9ea415ee53c04a59e5561d42b18761f07e7915ea109f749dbc7e268099f23139a2ae589213e66dd1dac63a9c1a1121cc9a534ac5150f70c7cea57191b58d7da008c177dd021d2b43f03b4e86fd0a0496bd604a10e0884965c1866ff75a6a767a8df45b7b16ea066c774d2a0f534a49b25e1628dd88733387340211bab608761b8414672f2cdc7b4873537baca8cb0380dd500192c5420b9be4681cc5365d545eafad4a24ad9921986c9a55a0a2a851a747e730234cfe127eefcb761166cb70cc9bd354208fa931ab2cb90e632b55516f51b7329aeea7741d5682578797164450c83809708334361bcfa64307f21c2fd41a67220e80ff6e93d4e05cdb11072d7494512010eef6090eded91d01c94b2100b4a64c42d858737b48f61ffab34e03f225ee59d4ff4b2d2dc74e5950439c090b513c4e6b87d608c9b7782f426aec53c07e6c1560a4271fd505da6498426341f2a9fa3bc5c29264e8c58e1b5eba1b72b5b63d36e59cc3355a61e4296f763153398e842a9d68fb755ac9b7422965701b7a14716fbca8f62951c09a4df5a6005e52237a6391d12028c1cd990eff74e84f8418206d7e189e71bb71887497c9251990b33965ad26f647bb11cf139e9e7547c7a83479a81ac6368fe0e81c65c99105eb6bf322e62dce2fc14f781baa254e1b1f45995aa7bd895584f84364810e42469f6ec70fe50ced37c3e4f84b49134f7c74066757ab33404c7ef1350bf2ae925c4d1f2472ba006362a2ecbb405357a439e5730f24815d4e39baa975484c65f86a95576904718094793f186364c06cb006a7325e053b621e6a40471d2f0e75ac12a6b2ab14a1a3197128f2fb42e480077bef77852c4b66e567ea05502ebaa8182191f8ea39d8c8830e87024773a9a11b3f468bf356dfeff6618eca5475f21e664a33593c683316dd1ea8ae0f5db71ec53236fc94365079e459ff7b901c4cc4492e928cc9275a1157716738522dd1f84f36f135196c58ef4d5a4d6a0253c5d1132b1fd7fc01481be9259c5723370cbfca0897090b08b07aad4e79ab0b47a97f0010a8dfd36b62290d38d03e0c40baed921f5939ea6f67b72d40f9b6152db67b06420cc81e682edf5773cd128d023cffeb7ef12c592d285e302b3a8290343980e078e183c850b84cec6b5407ca6c68092f67e8ea6f41c1846a515d9e9104748246578d85a7339ca4cf5eaa59a90f7802ab7179cd9665d801de4368883f0910a1f068cb10f546ded553540405086d3c9e2877ab65f03b80e79c0d889fca4c82ca1055a218ec2cadd3d72bfac1ee0b360d7055a9be03734dfa9a76ccddc57051f4554dcbe3b237569e381fb81c16399c524c1f0bc75c544bbd244d0652711ba2b12c6e468c7f14d50f0d62b3048e69e52c9d64cb2cba237f2a3853cc382b384a1a442f1a026a35d9ccc426b54dc84226ec301a61b0802970c11b1b3bbcce113132a84e759e231f3f7cdc1e8e6c703a6c8bc468d6aaee39bc8f7d348c89b571facd8c06cd863519199ab926f7d3fc2d5c5f904cd0769c77f0f67561d2bdb35d6680dd25dd1ab968058ef6784c86954a90362504fb559016e29dce1ee6b3e15c5c88c80d372aea7ec5364d0c66cd502edc910971ab5d9d7cc0b2cf61b663f76ee7ca4627c9dc2472e731067dffdebd6d2b51541a8a0575235298f870c90c977acdbca50aae696906d34d7754bad67925f5eba42497795b3515632c62129da2532c0d582156f4033c8bd8e64f7182ad5461b8e92e21504c13f3e9473552cfca6f0df1fc6ea2a9fd7267fffc163d6939007bd542266781096cd238210db2c70c37be3499030c96c8588bd78615a7beca1ffd3f920d3db0343f0ce3661dee2026217705bd6b97be5948b158137117cb0417dabf9d6b8a97974b25104a29f85bd96753eecb3b989ae220edb05538c85c71357c0e1f33fecad34dc52f8b38c1149145690755663ebeca6e0c44491c5aebb469c64251229d3f61142e2c2a7675e5e0407911d11877c006416c05787edcbb0074a6bb311657a0c255f37857504a16bb1d16c68800789c841bb47d2c16d3d10f2143833f197a2a1255577d421b87c34f7dcbb93464e205aa66972050587184a735ba30750746091444017e9f634eb674537f681f7c1829f62c307db60b6fca1f5eb965be71f30618173e4f0003c36a862a2650923165df1a6624ab2d2864f62b63bbac5e1de6dd935b1415ab7e60da6b4d047d9667065f063e92b3f52e0490be4efde45561ea5b985dcf7bee2d6203f5384cc07d04a6c6b21734a9712bd3f0475f48e0383fe3bfcf351d64863b29fe62706ea43353f0727301956edc114a4eb06771caab5708cca478cb4d930cf1383333535a9a3e769b58110521da5df3e0b838987ce902b45183370015015d2274286f9d3ef635cd9d7f3bd7321045583a143bce6f05770bca5c158db4c57a8173555ca8256b742ff69028fcf88736760a865fc7c0b46ece782f14c4f94446a0e16a5752ec6e20f3dfed01335005018edbc95adfc5484ebcbf8c019db82101e8f91d24586fbc3734dfc134b8c5f907d192805445f2b110016db660087f18480aebc4297fbba940127db60e087ad3641839f971b26d93095c40fc436ebbb806a4a9f92bca69f65f61f2f852ca06a95d3372a070af3fbf41bbf2df3e59ec465a5bf82a05220b6f43d965b07119424976883cd6430bf2e678137d835ce3fd6861830f270ee6e01d1d10e9e4024b8d2b6983b76914cbdabd3f794eca53139802542d5d320de8ec0e662256be57ee014e5438994e231634030cb8cf455a9a841f6786c20555c04c5f194f39655b8dfa74560c0b836376d1d47cade30a49b4368d6a70a9ee0c44dc6a785a3bb13123206e1f10a9050a06d8b579c42a9f06ef8b2975872f9e340d70900aff84f35ec974710b0a5d5c2687f89d436e5a3f0512f04944a2bc311f5d527b4b5b8db949863ffd184c98df5fd4bd4b2d03205547314a074fe18d2717dea8f76f6b77b937f8039660a9e73a09314e4b7dc7c6f620b276b7327b84524876a5554e544ef265b5ae8538a7574a16fdf44f112175d63f04f9571ccc88316e5a9b1b1d46ca0209a6524e6c2b262e7a5ceb0f3b7674091034ffde6dd86d3a6f41608456677b2707d8fcb91e67a6e3510a55aa752583b07974520c74d16b086f0ed57c2a3106ca4caf9a853488e7c656cb282a3015c4e91e9b4d6874f714bd6a0bda465d68908c75df051470e377cf0fea3dfd72710fe75f54d79a16297bc45310cf69697cdf4f154a3b426f8c73a3464aee97732d16170670090742ed700227442b6a76e98a02749ac1fe140df17925b9762e6ec41b43556dd7257cf1fbf2168e2cce254077266d369aab3ed9a28b5b7e75142c766c3823df1bac536a81fd793a28965e1f3a510e363c212dfe21400c994fbc5becf2ae2e52df0c3569430b57e0c4703e25e288083e4cf44631202b3d2f406f1c6c86172146363a1879a6f02252aa7d1ac6bfd65922706f524aefa415ed322935b98c8376f7e6b12ee5a7673e9beb6360cc58c53b17047b66dffb7c25a4003c2b20333d71e8134f0694bad2076c17b04e11f87d70c0692b678d6c6764ec607c432226db5e84a501443b99e5278b605a55e8a9ea7670b8755e66bac8649662d37e70c0d77142cb07213ad2724a5b4a230bd6270f49af3a4f50b4e1593098cd9c766bfb8c690bc4841f9febc6422616bc73c029fa360204db4db3242a303cdadb3353e2134b93af46516ac3d86966bc515d282148751970a96bbe2d604b872cc1646695bb0a9b7cc53afcf563291e1599151b1fa83f3e0b82741841c15b4943fe2dc26021471a01e47934d71043bed51d0216e6c978272d4113dbdffd7ea0b9157a3e9a5c0796570e429948f4447317cd587daf42621ab11a752b554f7e26f97648bd29e100e2723e3414a98c5c2b86eb01380862542be5ca07fabb4b22c20101322790b605ac80c07775e1b54637b31b79b84dcd43a3c72b5970cd1156f779c15eff091b76efc609600a28b43ec914ab09870bab0aada8c300dd5362029d1e4602e72ed35ffee2a52dca1d86227b4bba36bc48762d51670e37667241123a5464010c8e3344c670a6246991b0142fb3c3682a85dd2af7b79b65b222c73356bcc54b63b1442d2d0f8022e81c5a575da1a702e167941fc7710c56435ffd0e84022253d924cb1414c3f425c7aca3506ac45f63b729383e640a431e60004c225791de39dfc93c49a8f6174211703757b224bd79d414f4200c730c10dd1415643f5a32589cbc6e4395d41f37aed80a25ba71a35be401244caa917e6864143066c18f9c544a206c627180385a0c7ede3182f2a05398c08e01dfe4db20870b884bdffd303a6269e14d1732a064358d8a425b0fc13d242810111f4d42682735ce0d1f34a676a7d0f1058f590630e70506641e1412328721ed2161bf2639211f6c56bee1a669837e8005cc0273657be3484dec85ff4674ab12707d5737543592cb405f210a4f89b9c45ec3757b0cdccf277568adcf4f69078b203dd27908ae26117d94875f34ae9a0e50e79aa61194bb0d0a3d743d1ce7df890317d120635c0eec4005621a1445dff44b3908054e79cc867e6c294e6cd02bcd514b6c29171e6fc3711e96f351ab4ed146b0f78406253e6c2a04d65d545a66e20fa669b37d99c96607e1f8ff62d7e9a1246ce8aa0fe2eb956563e0e170a7274d21f13e1021b98a301153f6633bda240f03c5dfbc7326636a2149dee30afeed4549b66bab7064f6bd3053c6761425637e3444fbe14b4e24821a2fb34d450effea01a20938678a7da109dfcd5f4ca72ea32099465c32867be424ed9b862da228455f54d0ed4681e4392c551b1b40f288656daa1c9a61edcffb5ec2358a2f7683e65613c1ed466576273ebd05597eff0e6a3eb6c24767ea82e362ed66395c0ec73b39e9c0d1430b53f33ff06f6b65ecc49576e1dea67d7ab9a17846dbb63e18a9b20e9b03b047c6b9b63d17dc2a21eebaa053e1e71a0ac8a3ac54aca5251a7f1869311777c653a72b2c4c57061b1b18390e1d882f51319d9d75041513e57b7e87b2628b67a43166fc220266a73e167577c55ed9a90e613326b25e14ed920c23eeea2c093c077dd5a6e651dfc2ad63fa307f17360f5457b699f50c49d4af45073cb146e04281154289934edb5a5a5b22de9e19219b953c8d72672b0247c353a522d916499246758f3655252bc4f60459ca824c3c8df2762410975f99c3cc49c051730cadfca55c5034533db63b2207a42476410d9e34118bfa486be5e888681e87c412a1cd9471966ed476c063f97ab1b70e4aea1d825b38158e75bf47c4221bba0b225c905f378e5a543d7726ca2a63f9432084aec27080121d05a7793171bc219c586cf7836f589eb220a094f9419693ac00eff01a1a98dfdd1f64f1466d79ab8851b8a94b4429da1a3fe74565353ec1d82c141fa05328e1cc7b433c474849afa67b2e2b014e22cbaa21e28e4f71a48f6e4ca6bd1213d205d52c2c88cf25b0c8e02dc9a4c770378c68472574754ffdc0be1ba66ae22d3c27fb6ed219a360f108db51d0c130225618de1f0a7dad5fa8353c0b4175540cb90d593c80c50646e056760bb96cf27ed2afba3c7418ed3a0af38102df7029478e356b6994874c4f60c4765ce186dc281bd86b6126a1ba0f77d18b5a58e59721e9a9834b48415a44775a91379554ca59fbfd5b4bdd32765d2addd41244b6e906afb56a3b4ebbfa2282698a0ac65f446537bb550e310b7f49d7c6a61390f47711285e95026994ae672c7c9369712c376ffdc73b37aaf2a45234647c4ed591e1347a66764cdf5a465a2f504c3768c79c480bf89b0963d31f6b37ff12387bae255324bd6b79a9b17f566a94fa7a4ab47341cb8da21bb8546513d98cc57c7189756ad62e982b8017d876f0c5ea2ab1be974eec84307dbfee97657e348769a447837ac311f43a81ef020a7096e148120f15211dd88443ac6e8d7d6674ca333c80a66311eb505f8f99297809466606443e914664394e5f2d186841899a7f781413ff5ba20f9567e7c5562f46629f08bc1a4208d1b3b405f904d150e24861005e680903b4680567bebf0c52d3f2d2097c365a796028dc370dc77161b148686899060e3fde807f44e9b8a5369247e7715b58c420170f0e6c63ef77195cd5a31bf114c957cd18037122fd6b0a2ff3d12d911a847bcade606d802987182ceee25ab20367574a282d338866357b95a18f54ba8ae6470f31895be87eaf035c550c7afcdc973f08e2b749f161e32ae27ece3adc81007e368c17090546be0e80a32246f5a2e725a41dc82981437454514eca575884e26dd9f8395e7329ac042a718843949bf97b6178277b0873c548f3a69d74676a974514a8955305a4544d50e33a406ed662351b342450e583e958824b4f6f452cef1cb943d82c98e81812b5f230725122d5603eafb97602e4df479e35321a872cb126419b5a697c5e7d5fbca80f6c45b5820b8354333bacc31a0fb0bff21c498268319eed3f215124235e2a30ae7c04be415fa067fa2dd35ce751849aa209c6c7c33a1daf363e1146506c9287dc0525c74239445f24181367b835ae5c7f27b331895bd1cc5d5eae9d182fcbb7333870f526448e44de41863f5650dca95b7a93178736e901eb494a2e5a014a8ff30411e8146f9c86ec286be57553e17b6e4c831c6e36cb864461e760ea343e25fb0fe441f809f0c12d7dbf1ecf560695ca63c674df1d7257f875349c1c3eee3d2c5aea79d60393168d0b390fdd17230b950bb41b617086fb96543767846b207ecc58a10b8c1e10a4a721d1505b08227cd267e809da6cc547f700cd266339eb33a71e35329f254e24bc05b948b616a8eb984705499a13e9ef602f65fe2f529ef1dc6544ab9e46da47c26dc5a207018ec0a22f490bf829e5d2c15369992f2c8471970a3d323756e07f331566dfdb7e3d541d22d51d093e8d47a1722f5ef55061a4d244e7990716ea2f4e21f753e648205a0f17c01c026adaea2b5aefbead3ab0364c717e694b26d8c3ca282ed7040441b3ce14083bdd18f4f8952b336c08576d88ff275969a74a6c9d594f8629624166f7db14f0755e10bf92907882bb575379145f1ec242f40844ebd14bdfdf236c854f295e8caa42697eea0f00827a697981b4327681cab82ac5e3ba7a56854261dd778d06af0f9c3c50724b0affa30b4876327060fc7c4c2cd4aaa8199d3475720116f6105290ce11f73a906c53b5941588a6bb46cbe32f174254373fdcc4340cdfebec16615c661dd77757730ae9af0ae797035e0891bd2869fcf700a80eb43eca41ae6e3845912208a88836a11f806b04ee4c62f074fc52a1324d446981d57651e6a22102c86e0343f6b510e0e8127951d0972bb06d1458af59eb5e53c1b271c2b5c45b2366c42ff3df88365780fd008d3c2c56538e6e37e64d785c72acab12d8a979614debe645cd1b7b09be11383c1a8c9e3c906dd61eab56cc728b8d1e2c2abedf7bc5587764c18eef066c0bad4589cc495e44c05e0178b86c0126d95328b6b6956ec5e29351e3c8da55029da053a7f3f51373d3da485e248e50be3e03731e4e013510a07030ef9f25488723283ac1966250c418e44bfa1844301beb4248679d621c3145633387029008253ca52a44723a3172270c5ca941ba29f1ec307749b3730250201127c8963d2848752a33f6f9667171e1d928d9938104514f0834b41080405d9c07533d8009795f44c3452722112a35413c649d56d16eeab0ba6f4373c9775f6e0b5a056ffb1ee507222908a50b426f2d790b3eb0b12055bb8f6d5d45423e6e13df1bef76805af9befe0d6feb412a0a1f4e766028f32e4064cf148e3cae62ba99a46c57bf017bd4f776723cd9452784eea318c3925f757674db643485e4164019375a7766a16c49dd242760b92d47b8697676af17a43c1055305ca125ca5fdc3d6d1c24c4064339115b050a4ba7489170701241a77927da73eb64a944cd4a101b6b56b3a2d049ecfa9f049a9d955be2b9db37382d3d3a0021714b3d384814524ab227b7155669b6d1e843223e1f1741b4d339594d404dacb8c701aedebe117a5069233355b243e5dc5066e3b68c6ab4bc2174475f670db0f0380db716c2781e3f2072ebca9b594d3d843e446fbe5431433d5441b2595a2954d24ff3b9a64c7a4d674362c91e4456fa3d62b72a387d111b2b0a0c30355b82036d4cc85e4b12ca3e325e373cdc5a0078c33ab416585d45a70c30e534752b03f3d31b8e390627952d633de0779c3a35417968cd513d080969f562253fe72625ef6a612f98361969811507b871ec62b0c4b93dba6d1f5535281e04f3b1cc7cdeef9e56e26fc03afec5da3eb7d3bf61ccda0e04423e3c58dfbb88105cca41537d5a2f07ed0d304184c1fa57c95d423197b0174a04b0d95352eb401c827dbb13ec37d33562143f1d1bc92949edcc7e0161b0204c32c7192acd87b14d2cff781037bb9b6e48f6844b599e50068d6dd22dfc59f03549969e0bdd970e2a83695e01b04bca717ad7b25792f87c3855af0400313ff6506abdee122910e077e08b025c6938a16133037736e7bc8c4d4f8fb36d56bbf24492c36c56493d2b5e691b80331591e9142b07760635b045690028883f16e8862ebfcc137d9326bc661d983936040920114bceff13c60bc956161b93778a04c8282d4d5b4ea1eb230175a54f60f0a78740bedb1b3dec20aa19ee60a30d72de0e6b72a08c2ba7cc520a9da94c1a85b4ee35f4dd917531232f49b4330a431b20f665bec9fb00ccbbbe70ead9fb45d2b7616df854cb35b831f2569e677f0ab94c79659459f44be17da178cfaba24cdcfa225ea21cc977e4d58d72cff3b05fb8c802070c2b236637eab50a28a40c516e352223b6b7964c7c805271d322835c251c0c0049b6f5465353bc60ede3a707529199560962e349c845956bb21f8b35e27ff230886b0c28a80158525c18aa4495e198047f1eee49d14cdc4890bbf57273e2a5361e243a664449e1368bc9853d8cab0a6da0e9ae75a9b8d83984fbfb277a61c61fe470dd512fcd074ea3f03e532d480271f85b903bb3e9425df230842e9edbd624d294a355cb43b2532e2327787ae8cd62b65c037de9aff25844509f165af9a23321c7a230fc782622b7a2547b6ca56b42758ec81984d05d68cb50231e3bb62223c7119d0354de905acc798a57cdff6064d2c0fb57ec6e343943d5a74d73663a12bc8e6d093340fc631d520d6e59fa6f55f162a35210fe2f2a0ddfc04f36dea84cb698df047ba6f44067989312da02434734719f18fa050b676ec10e259384d92e846bfd5142cc8c5a84239023fb2c5571037bb22723c17844404c2f4da0b0704bcc04c65e094c1341754f2f67b3de2c4b28089b04308f86256e225106d36aaf4f5e92ca519d7ce65cfc8a7466414e7d0989674806fc022905614b5831a1540b67b442cb3a46cff31f943f814236e3b6343e9e4f1a335207187c238e6849da173cf609764b45de23010939992465532a3a832f846b566ba23530f7e11d584744757992260a6fb42717a1f790330a5fff120066715fd32b3e06dc45043b15332c1ed03804272255a14902e0db5ada2daa3cb687115de6fa7056263d67603c87061d828b0834cb0913550766ff742c540935b1219e4f9c20c118c2da3c5907b7a63879ac655347d4867aa0717e0d42deb80b88479548ad68081104189035d1296f0594707e3276328c19e4dc4527531ff82646812c39a00a76718c201c3df5b0815831c7e336ac7ae53b68d0267d8328093599b2965e84ae814a084a3b01703be30317767e56a41c6d005cd97e5ef522384ad009e95e7f2735299681bc4ad7f5e02c58dafb6654588c32b9696a5079d9ec0495b66667ccfd895cfe23f673900b592dde4b7c1ced74e319c6a1b12baa434d4a06e2ad5523266a28d607d070a75892295332601e6badaa612ddc9575d63afd4071606f1e3a16163bbf10891249cc14775e616557a2b6042a15af8a4e2bda737a0fa7ce0dbf6880126aacc9542af6255601f3ae35437f536e5b30a14e623e9457a0c2202e6aca722d0b2f1d62ec45b2522e17c32f0e0aa91724621e76c17edd7517ba4d559406ae451cb0255b4b96d444cee08d452daf170646aa525205fc054f5154c000cce9036f4efb4566d10634232e58d63e23e61b1a035a033700b2f576877b3a3b487811755f862d56e412610aa86fa75ad00f9778302d67367cacb02cd9c02419e0702c41b7890f4a21f5b15899b9722d8e719825472b3472759b8c6a2349d802733dbc6b8544816680d5a1284463ed46a8a06c1848d73a5152778f4525128258068c343d88b245095f90660fb41b620021570e0e3d2b6131723aba362198a116bd6ced34bbf70a2341aff27e4918d666d3bfc9099abc567ca0054b4d0ab80164788b5d375fc5aa580ff9887e5f105f3e089ff0438c759f67c4679c41ef67f40934b6615cc9ca920cc4f9e83a2e54fe35e43acb3d97b9df15f50ba24ea5dbd551f5b60826187d893031812255811fe333f7be8d18deb7182a2fbe165b9448fe51217df0042deaeb0915b09d6c6010223c7fd0521b6c529f3402e7cd4fd7bc3048a32873702c31854257be03735904044b1017263ea066fa0b6ef357743f0f0f5d1ef3e164beba734e23a931661225345878d22a414069633016342e546f0c5f41996fef335d9c842202ef2356330db8581da0e2697c87797d2b8ae7484afc342cd21a252d31c8143caa0f5822d30c2e325e5249357f038612b92f8a19b00dfe0afd17a0200f760e2cbf98742c2f198725e3d6770eeeafd51d8eda6401e09f6f1db4db3d0d8193892b86094a6b22b37b68ddc8c6268c71fd675a2d9d50f45e3c73c0a0c21e9b8d1833f16d1f1745d91b7573d4b458a055813889c8ff5dfa547b6bf2483548e939230f2f3ad85fa3d41d1608773e671ea6f211849fc341bbe056481d3c1967911d34139cb2cb5c6fae2a363fa76c31cf1fd9543eab026903ffc932b657545aea0a246fe1ff131113ef1856ce888229acf68560ef312c3af042014395c1f556aecdef191dab09161ac6f84016b14a5c2970f8771ea651505e35905ab519f019e0406b507955ed7a0a7cb76325d4c84d1db8cc615fc7c904e09e573b85d8dc2f3fc017181529f235ac43b778c907515ad0ec372d4941c23a97c8fd0c29a04029cb08c125986abb67c932cf668b57c827bde7817e4ed4580c9432ac11ea34d17a934eb96a0297b014d79d6c5d353b7b2f3539cd6dce6c8f496270c848248d055ce0af5253c96d852cd7ca0352c0cf427e6529c15491075f6992857240d6aff019acd6797a9dd32166b9b2c67ebd864d2bc5ccd43ee8a7786edbf42d13a165094f0443e545cc926c3f0fb4ce714045ad0597cefe788892321a4744bc0065ef7a1397ca2f14699e7862eb516d522886932d710f7865a7535b6ada50d40c32599b3069727a115a66e6747de72c5c04dc8c271446c70411c29f68d6256222215a0765054804348eb14c4a7258645c3febb81d17999d4c9d1cb840863caa2cd290fb1ff660405ae647836f0c782e60ad1d757b98cebd5756ea11391c23e66f73167539a53ee17ada50f169e9f4610e3068617ee38da81fb6e014766e481640c2e1a7083fc34217a31dd7023417ae3ab2db15707ffb7534be22d6363899b96201159d183613a52ddc58ed155288c1225b5ca47834c65711f880fb0a250f0929d7374002269dd93ffa2e9f430378e1277470a079d701883b0ff261563ad23d4b19796955deb8ca0c1895153f37c342058d3da20c3515933cab0fbd3e991f386637ec786af418415a78272d45e8221a25d18ec670af74b77d2b8b862db6c6a1233a639b51f089b7511ecce40665d60038616c80599f33f61f171b9b109e8b27321f992569d74b4c082710a43442b9145d16302a607c0ebe720d6d7c44fa44350e0a228f012fd17874ce400d08a06bdf00a4f9604bd37027489186a301e9995238cea52c6f8e3b51223e73283ed051e726c71c746e31b8144669ff5574dcdb8145ba1707375ce8006df11cc35454bec91228d6bb264780540fa4e4d24e4826d07defeb353a99ec5d5328c23c0eb7d2bb7a2d64d44970faae6a9d2e5d3fd5391d1cb4a7dd7b7a86f656e7a5af4b179dd83402745e219d036271e7f66f76163ac751bd337c24c4aa912f18f79e7be3e6e635e731b46ff02d987b53ebde70c7debf39684ec63cb55f8168d24a9d32f7ef957de4b1072600cd7b00245a656495c51a20fcba573777211c47dc378027ba1a4f6a063c0537dd6edf7abe518f4055e47a1ecaed2957c7558a3c5e9b763fcecf85799030746a6d1c173ad7ab5c11336d915bbdede93bd66cef6c4040076c506e4a5671f51e6339f43c68faf41b2d61883d133972a644ac2c2e48b6c974732d1c3375e75cfb1d5d19f4654b74727e8060211c023d32783bc6420d4164846e9ca0f63ca75d053d7e28bd1e691bbd6d25b6b10bbdf76060027d70592e3a1541c51e1a0a47fb716604fa4e499bb4e21dcedf7211ce7ba957ffc60576cfc2715101bde5198437e45b2b4ba833fd8db42d09a01c24a687d00c98585b0cc8748113118bed1a8daf722ae66e212f27703d561e9e532279b1184d0fb30c5747e27d4581f11c292f184872405b293a42a45f2b66a4c94151dca52b354f0f6474af482fc1ab170f827d94364164574d92d3bc1d1f33ba66a223710a47717e60ea9c2d2b1f91b9034ad36d7653720553fa1a5a4c12063668ee24d701486dcc2f2d435c2046558346c67ac92627c7502fb9acaf66da19636be2dd1b7627d67f63b65c322b02adc57be16a2370c4528c3d59e5041ac844ab7d6a7deb1c8208e96d0ce63b0aa35fc60d9398e424707c601961b69d5a647abb6526f3ed4d9738b046b81c1667853a185acdfb9d7eca9e585481a3aa19650e421cc61f5d5c6b78211a4b68fa5cc1adde18a9f5675e933ab53835fb007195d18025eac90e1d00344f0842852b3fba4c0233a95ce27d5a857349e11194404387cc62cb87bc2f7e31e84501190b4d7b6b9b78c23d680d9ac77d34497e24615c74eb0ccc4ae92e1c25d33578c3583fe8b8ba04d5f7e129e2a7ad7d3f5815090e4cbc5419f4422c97ea1925275dbe349a77304048d74b05d849e531160cb901dc47272b2e461355bbaba21602bd0420c8edcc21b244344bc2a7985c255ede62ee9e764aaf5f813cf129bc3021e3815130361a67535c1c375c3af32da4e0515ecd3b3722cb0ef414b6e0ae04c50d392d8738e145b46b232e9d2b6f2b1b137c1f40548102753b403ed34b0d7c5db0da72458a884922736a741deaca0bf402c46dd29cd152fd985e1aad58132fe4fcb937c2be7743e76dd103f124f233d1a85243398a7e08655a7a4c83ba8073f04d1310c22e3456980990326652234935d2466e52523e449b57f6232ae1327df77a9402f1387568cf39f16d907dc51e3605db4100952d657ae9a45d79ce165805df3d357c626e587f2bff72e9ae8f43843010097532692828c309347299ff41c976b021c4d9fd529045d775ddd5485f991a7f7c61b92e03b313ff3f8be45c677b81ea33a38d596f6fc8de4337adb727f1776561cd69885104522a1fee8fa200a87c3426a13c354a2711177c88e3e6268ae7867b1067a162aa710534564fa931f62dae6c5e601b558abc610ac331c0246e3b7c298e0bdf3d2b088738809ee803a31c6265bb35f078cfa35e228216de43b707301a9d33ca359f399a1dd4196419f847c94f01dc9b4add299b1b1d947f3c2317d822a77e8a4bcce9b635756b07725d20b105abe6d629fdd3ff3dc542ca4acf54e72fef031f3c4a2c2810379e090cc04d4726557ccd735b3c3f7c95db1f7982f48543e4be8904af68a23188216f58e5061270dcc50515c07ac5292df72e5feaadb91cac944e1481b1573f330734280786f902ba33032efdeeae516777832da6ee3b47f29ed4785ba8b362be7aaa0624037529e711113d08156b7de9d18d7d76c0ad01dec7c84adaa6e406832fb069764b116a43cdb26cb3991a6086d21968acd03942b4872c2f51b31c63d06fcd1e45d5da31e830581a2aa208540f04ff56b7b6b80cad636a560354200e3910824c03579073f9845866b90b3b753e565d48ad194e59e75ec439b1c9932ae0e83178b14dd808d95017709966ce1d582a6221e7c82a71a578485f1b0ae9382351fc049400a23d61ebf87a713e13243270c229e992d406b2025634f5b7bd0508e5b001074851732a8c3233a6a3d17b78c13e439aaffc11e6a872040376bc21e83132757f5d4f4cd39206527d297f648827fc064673a254d30b064c98e1be36aeb5ad2fd10bf92707162e1621225122439b884c59f662303982e2000644c136c495711d31c9162cc91eac0a726d264fd4a87c48d5fbc1023f55b749bdf3a60bb946250cd5675b7d613c64724d07d919d25d9c703f29c66895f58b235ceac75a98273617b015e72ff80cd0216e075f17a6998640adbbae668a9a313dd4931c764af3f930278d52566e97e6733e1ca32e9dcc4c46a0ac223bebebe3136296240dd188e70ce538f04691ed5907fe9fa11dd8219b5a607cde5f811f3911563d8555df51116b07f5945062e6ad5b67fcdb2711dd532d67a97a4533cd0a7cd43a391b30339d0db77e346a62283348d50fc318002e0b651922337dba50d25dd15cec0515eb780c8dc6280d0961a24df0c72d01a552461339faba5ed9f26c145ceb0865ab3bf41107bb815b31520a60ccddfe16e4e15e2623167519f22f7a11eb3b85146494166393e00000bfa51f7c54ee2f75d5953f7211ce2d47ec43084feb860e084f46660b85eb965f008d175d7ca14e3238a8ae0c60984a41ba3ce90331d90270b02fd5722e131e44c51f8e31ef1a9b1bc949ca5b8436af011c971056b963bf33660a6a307ec8bd0d4c3d2139ccb67e32b2c18e0a4ad1934991788b281d12c55b1490b015662ce2663972db4ef87f191567af2f66a267fd372ba7432e96d44e2f4499c101e6939f4b041ecd75849bcc335dac14787109d763d43c3d29739755266617bf2526c68d549f40b85441e51674d3913440b565de4428f583382d733c117285df6f8f2a760bba51034cc3fbf364d7083f7d3bd4e4008ad4176422e8e33a8b26be267497964e7684572cd5d6c6348e79df3a58c0ab409f505539f6c955562fe15b6bfe25063df281672e65437c5b1dfbaa1353ed7b680d5a0715221c2d08f4e64f558f045b041c22b369bb6eac047ec3084f6128954d84d1df4507ff372433bb2b0686cabe7c6b6ce8330722680bd9e956443c9211475af3583f69b06709eea43a3ba90562192ca3be624687e56f03a6ba613afe8764c92139605a6318428b756678a73d9d559fb0292eb992486365997943d02bfd10d0445a7e58034a0253a73d5c84ed6c2244b3b64b0efb5b765f200819c31523585912720f28aa0b63904ea01df9782f542277e03d99c29c2428e1e52c58c29360d7f3c26217a14856b5abb0482b44404bccca6668a0b63c0fcab8a2046125b4569f99e00a62ac895cd4d8b67a74a8a222b9455d7d62c5583548386b6f8ae239459d4acf1d941a335daf5fe946916eb31fbe4c0c1ce979b90844697c7aa584a8182e7e76583ba89c01c5975b4d7709f002c486f87d97323315e561b47e051ff07b1662e00e0e1a6f274d56e537684cc2281d602f2fb6e9310a80df32402f23c44abc970f7d3161551946337968b9f2b416c7076a6c3bf623369a862d4c5a75ae3029d18d4089f6ca5419ff8b319fd3ed3c9d10f33a12eb98671a455867c8e59c58e4c1ed7dc255c85c11ceca5c6bd080150fe10662e7f3e457a1ae436a08fb292edfc9ed1fdbaaee2f65477a0294997b2fd213235366944b69c67c787e90fad1716cf18369821d52092bf7c729225e957e7981f5466ecfee589698350477f36a272470fa1a5dbf1626e1757873cb1d2e47731c742c22c7b4118a2ff664983af61c6f12cc11453bfa1a28ffe640dcf952321294a808f2b3f43bfd236244a622152f1ef75e065fadc70e69e09f28d22fdc13b973816ac4ff23496b28c42442cf53341af35b2c6b34ea019999340545a05c181af31f3a6b0c0d359f53c379c626d3192b46973babf80d18131f8266219b4b7a7cfd75140687da52e17a3f0c5c996e1f54a82f5f988e0d35d8948b05fcbd281870105b472d0aaa3ce20be804af8cb44a7873240376c216732667e527c7edb96477e7073bf9e61b5cd5027b40d24001269ff4b9723d8e9e563c4a31043574dd03d8a0851f3a81194181a6be6ca3457b56f9c95b0212863b4de016587729c2cd0e8525381330add561a1af6921018e6840ce43ce4d9c71c2764643773b59dc29063605811cd5249c580c33537907dd1431b08e4e5ccffd9916152f490c859a437b60d7b67882f8836c4c1a7070000fb47d7c7ce538bdd5eb485baa9e00b939745d114eed26b2e62d4f41d33370f7769e059ffd21258b3fc946184b8f7157ba1c54e263c9517a2f2b65ef4d351e473dc140898e48584423912a57d55f145a9c6a3098293b61dbfe5e18edf62b423a9e6e7cce19bf6b772c893518f9451f3c458c26453c2752a7280a4093391e260fd3ae3afe65bc0439d28a3aecf80373c14cd237786b9619259eac1fb0ab3275060f2b46a442d64d743bb118e862344396349159480b1d31df8add61de0bdc5277102c08905f9833c9d35f5a0daeaf3016678c68972e20498789df3c59565a408b6a65584d24f56ab4d7222486a1ea3ecfe48a08f136110e8e770377aaf66f0c967c8e5fab633876cb25b22daa863a74a207fb41f51806604f1e4511bc41eb5bd16b4d1d53f83c47d32998680637f33c2550cf030d8029623cb39c2fd1fe416aab68bc5e16f8725e08479608789cf10d32ffb05f4d57ab6280b9545881d1f758288ee53a54de7e37a56c985abb4aaf4af8de7455b0693d168ff642572e4edb32a06be45969b1294c40844d161887b86e4dbe6661aabdf06ce5dd0f020015905915a75817c5df2f5eff35e12730cdaf664e72d42849a2536e3ecfcb1dc7ba8a1f61d6aa55fa626c65780650201a2aaf46da8da032f4195e49cdc5fc761df8a475aee7373d6bcc9d373bb22c0f778fd32a5cec721a0465053815b85e0c7d46bd1b606d946b568386531b14345740780c6cb82615197fd195030cb33d4bcfd815235abedf4096bcc844b3ddc533e5895f13469b6828df28de2555d18d4b26424a7595a64f259325c72fc5b3f957e843384918f06b0ccde402101a228558293ddc067b906b559691ab48f315a36ff341ce6a565a90716ee32e44b94716653e5f05229bdf673da330831229a3b17645a22057ee91b5532598ee3e721a4849af337e3fcc01470d1a1d4a7aec6e3c09c036727bdf0e4f6dfb05cd12e09af10d18029b7da089c51d0808e3654c559958321e4e65a83fc52d83ad3c6506c8251ecd08a129d88a6e7a8d92412c673a332940b2340295722337669ad167be96a11e287bbf046679cc719e444d4a5d9e155fa593b840ea089a5c53e8f136e1fe163e16547e159b52c21e9c5be32cbcf93a494659b70f7aec423c5877db598ff74233e9e4601befc0983960ed875193736118dd12d04687bac153fc5edc2b19a8a8582d2b4b3d1771fc7be682832ef7e89d3cc844cf6572212a3c922a6a0c97afcd5cfce2296ec0788d2e8d63432b163cd0389934da683470bb04a256681ea409ef4f8b817a4c2bd4b20496f1071a5c324346721de55baf5d680cbe129451c2cec4712c5ef863b1ce8b2ce99ff1567f85250f2e47f5551f47006af17c4c7703bdc95070cd9466e4855a790d42dd7733d5721cb3725845b18dc536e25a2f4cd0e4084d3bd4ef470c795111ea51b33b8b57ee4ebbebe65f00eb4e1f112a7b2a560c3c15bb37ff17ad07fa412faed96c01de120f976a694c064b890a06953d6e45641b10f297802b62463b26bfb2937c00cd256b5643f07445e34209fe69d54a2e8d644738dfb00bd89929446649da4cdb7b9040046b592db0da555e7da07b35d203a96eaaff1839649bc41abf7320358be3965bf2ec217a59645f00d8131f731b625a06719b8c76bf53342f294cb53d37f770109846ea13233b2f5c1ee2e0091ef0ad075c55cf1b33438754f9b192240fa9726248f2c32c0fb35009ef9bda1c7d3d6f44e1292f21036ace1c522bad3a8622f83593c12d10386ada49a216c51bddb57b7154a1285fc7e6ce18086b260435f1f874a5dee90b3ccf321bd5b8e15f353b335e3e0b2e753d1c25751397c522378e6c45e7f9865c108f6952396cc20ecebc13256a6605537e03f3675a82d0329f895861ebe670748b90a8028f2af4193b99fd53385f9752b52a6c365dc88f76d6416f77e624f0798626f075e6a3d0096cbb5946e22d215df339ca629d415818a252775d1afb6322fe8b693df8605f4851857c70dae66c05a7babd0ea7c837090619a07912db1b2bc478f97102175466e91ba42e05639a5623d480521d204a3ec171fc21b4c3fa405cc9d045e2c7890469364869cae9b27d20e25c3a6e2c2b0db6e1232db8796f2c50be6421cb03ae559265ab5fd62156077cf45266837a9b7d039db12dc95acd1ca2afb828e083e876c2ad4f40e88d570e07df8f03de9b6d788a302d454ccb60382c97f95fe3c9d66c827f51747820195eef89e32c28d89b3463f51e442bf2a84e50797c1771f25d07817f98513124151b4f436b3d8288821488dbc2498a8ac76c4676032afda09c264addba397d515d57c941a217433eef113a2d74428ca2c6705e33ae6cb745402eaf02517258389a772699581fb8323420974902306a3f2106858c58690e08673e5d959a16960d827d3a2a81115987604ac5143d7d6b03120742bb283a5f8ccc0f62989a04cb0cf13e70ac2b2f1c13a84424d71c0493a4095d3884a712ba0b5e442196886a4b9b1e7e4696bd6335567e3aeff0b17e3804aa33059384681eadee06f8c94828972b912c4578d330d2252f5a7c2df91d8152797af7b38c0bd5cd527d89601d0eccc4a36484624442647ee43cfbdb5021e5d37a4d2985652c02d7377ba1e7420c07551e672b3c56147ac9ca384f12de63b405c8557970b12beee9e7677ffd6818f525716a8429a438303cb85a3d87ad512f19881cc026563e36e3a1572236ab5c5212196ff9a7ab1645699565c15baf5ec19aa45091416a0e87c96c56e307520a5ba6436e74a84b5ef4bb01421a0fd939047b713853e6ff7c902a8f5caf42987cff11795feccc146b52080449fb42a5350d53306c197100701fdee776d3de6b15192735773954a73141cfae372ff6be57ed99d603e1c33d4d340b3f1e1005d1320de17f095ce51c3b0b6085374e670b5234f9ee2e3dd74e760f753c145e248251c49afb266a091929893fd20749a57e662268bd77c7e77e2b7951b943fd6671619f19f2768308a87cd1b91a746766de567c3f893a207031641a98320c9eb53a32bc4e6c77cbd969751a4998062e4b9029b11c697166f6a438e7a0c81bba5ae73f50f5233359b74c15a341cb3854cf0f74950bf575cac2fb67d443275aedd84574f2324477d232b072ecb1624367725062c433d52bf247412d80dedd3908e75a4952d8ba2774b16d4922950b28437919624a8e7b0ea8b5c161f7c91831dcd03d324db4a04f0c25fa38c67def61a90d4368bd597c24fa91376e6f8902521be9b21170f1b30591f83654b1007b6500c65624ea30515d1a910a2b096e9c284958d11e5b577d33db5933245a32f46d75c3275685518730f074443a907b522d51886635d3773c086ca8b27cc7f6d566d4c39947de75a91a53eea8510e0d541a7524017113415e24408fbb3ecb61721ba836f8026887d31b9d73af44f18c6b773b813924fd672a50e6900c0c1e82bb0ac0dafc55a201745364dcb00aa5e4dc225ca919631cdefe18b75bb10086b53a18d913883874661714427896586804130f868c1514284e133a5767a54ff45ad750f2fae14e83cfe1297554991e6cc617605672a53a28020a6a6bb988158729df4679fd42634203207cc7ea6268111e182d99432e4b7a7f595ed5b1682e8c037d3cbf605b419f2a193907b3f538529add5df3160b281aeaa07370682f7a0d77e322f0c8ea42f2b9c728fe14611c43b785141da25c2b28755215f7ccc05c4249d766da0eae3a9211e92c56f4ee5ce47d7e5613830a56efb3d544cfee8c1305ea0352c0b3097b55d83a58c2100f3f9e362a482ec26c55c9bd7847e60b350418b0581dde1c387213032b773d1c741dbe990c72c06ee963fa77d33024aa1144924aa03c198da74e46abad741f8bc474813837334e91d8216a72f57077c12e4121776f7b1390bd3cc1f50a06683fe6356fd69129c235b444078e2615ac3cb51d49c21753986c7844b14a8c2619294b1f88f08b299bd1d639220daf410f0db279815d19376e2718711c69c94f07d1774df92adf673bcbed71e4bc4450eaff3b4af5c32956a81ddd449f9d6a682586884e4bafba1cd1816a418a7a864fea693b41f867d16185286c639e34a1378ed2fc7ed687a3677c7bf030952c4f717bfb3f39acfdf95bc837ed479b249038dbefc005dfa863161312976e029fa32c2021b778795a486a6aac8940add37126295a432d257abd180562f20dfbe0466e548e7053e1138262dcb74625c907ff781c3ae667d2551e54c4d67068fe903b3fa242053fd3dc1b004edc7474827e7d155974b94c8b6b386dee8c1d0e90de8771660cdf6981deab23e8010975bd5b0813b6b6e306fb425344c9b70938384bff772eaa964d00c14309d6b1de7e113da57a78dbf912fa5680420290a249a2d92728ee8fe549def1592fc3d4320a3b338f3ea1607e05210846639c758e2a1becdb2b2d51231f41e61a28eaea4320481bf963e0661e01d252e0521acbc56956fb197d2f9a2f44b7d4bc0f2ff62c0fa8b6237036bfc02e8621d66752bb2b694a63e05fc82e482cc1e02a396b6c8015a012b973b251220d971705503ec73638a4289d07fe3abf21402bc00994ecb53260d3321060b2980e51973576d2f0566ed188803a9005746c9c60a3502348be3cc69b763e2758e30c4644fc1fd7b06f4db41235716be8aa1188943662139da00c9e74b54e6bc6e45b9544f44875116043ba661634237ea551b87be70938412464815c2a29f02d5507df468a671ea99e6c83f44d5af5cec9320006bc01bfe5fa55c57fc13f63f2c76f2fc5743bc4a1bd20e490de1e40b4f80c86c5c419f30ed82c315e6f1ae03b541bffda0e664003571d6c7197142f89b85706e07016a6c4f54085a42220acc081165ff4467cc9d9fb00db24a56c690ac64c8c9cf41d219a406eb6b1ae776920ab2970e8fe7a9984de7bc622705d2af9cd116a7bc96d89948e056e0429001fb4a9247117602075c4045f119ef9690901f7259b6309030e357f0defb45719afec260a8218ae6a5c3bdd3724935f0ac6b0ed134e4b9a49450fe572261720297ee092347cc0662c96bef47defd66a7dac37881dda60517b4f892e5e6c99ae70f2cf2f144cd8f638badded6c19d2b30da18ce65b0ed39c69b7f5287c2d68084a1f6601710ff33d2baecc3e120d19d6210a524018f7508e65feceb26d455a2c05c79ced5e6cf682509e13f66014aea4240d792c1194b52977532aaf35cb435d4b7471220737a12a7188f67b50fea8f17e91d4025a42af0e4092dcf425dd60313f3a275a5d6f02786080aca220a48fa53de7d2845f43c98c56d4550c79596b3d7cf1c21f53fcbb0672ffd8eb585d1a9c512dfd7f3b08a49d5ad7168b1dd7112c1ae942976269b9ac4f9fb1ea25fae15a7d3aa5fc6a5aaf374608705f24c88b9e1c2358da23be92b31e084fe05215677933765cdc7cda6cf40ae870780d1fd8dd50f9e9ea1d8fc1ca09eb49124305af2d270655585f40dc800490b9736e2976432e3d38c231f0e3c276725157403ed780192015083b90746e59661409696a261f1addc05b3f53b9a205a0334c51dd52b84f407b8a489ed39e28fc267b358a23ab57db938800113ab76249052a60231e69547c90af5fb8c1797106f38e4f6087c475239f4a0cff5acc10a0f62843d111140a26c1b443dbc1937aad1f8b21a40ef7149b5e8341282aed43e5f9e1355eddaa637258c27953727b5d29ecd52e70b0e40e0a706d5b2ec63f0457e97162a6c1604379813b6d84d7db03f8827b025efcc557864d04739fc2bd28794d3e2f8761d4793539de06fa479154be36d120387e2945c68aa4106f0cfb3a7fc30265957b2d70cb3dd102b72da842a05bf17d51b7d735eec14d5487848a6f754e653b9bf94d5624109645c27a451864981f74e3592600413305338964586052e12f4987da983718a4dc04f8799c261da8a51e329fe253c61aca0b4836e83c2805d227a64d30639105da205b1bbd602ce6966f15a2f57d75b63c05fbd5f95bb6398a12df70f0313b63702a9da3622e48e0514dc9c46f1ca0cda65de957e23c85eda26683fd825d0a7b5c683498e45135cd1419ca282856f1a1124bb95a601fd063be0d6930fb014bc982792607372adb87201f84ce293165bd853f9dc9622a8d75070dc90d8a297a93eb385b218e2fb081964021e37a7618b388444b6cbe0b1f009f18c6d0b47d71165331d31ffe24d4d10626bd24d473ca591366c2a25f778a80537a153af425ca34c4485d3b302ca778de140a2c51305b52321ba8680847c6b99f4f0edd1c6266dc34716c771013b628f7422fcbab15c3725843ea7c48770aee5747cc65660436869e3374f2f34a939bfd07531b69191acaa4550fcd4011bab5a2525f1d531a56411e040c0ecf5c2070092b55989d2239a950129df4da59bf96f23ac3407d7ec73cd20e47988c78ab3c6c7d58730b28219d82569dce220d57067d5adef054182d8e8e123c9b1514b531f26da4c174342491dc19f054d028399929634fc01406e4cf9a6f652dfb51ae7a6464c118614a33456e5a9bc364078d61132f6911d82a084e026e2254fc0b0077b73a27335d6e5f571b55555ee53fc1fd90514d97e25b8138b105e68371442d9af2576e47d057229b427e42504b3540143432bb9c6b7eb2e87724d37aba450f99746833644467ac55b724f2e54732c792fc4f28268f04cf2c516556f4a51f392994627786bd15671e512ca7c9fb1d1b42e030c255200de2a2c90e2cdc931776a70562fbcd224b7a805c3ebb1c9d21a73bca535dcbbe404398ea7c86973270c91cfc2e825e1463a18583711337db3c142f304b83cffa5e6637c13f1f9ebe24c9aed30aa36ac31be7a0194255bb0f6252450f3efa6996007f1fd650ee71da2654a1b53e1c65e72a022d2f5703d5f91d52f3ed160a96242263ca19784f24492876a52d2773b2571107d4a57c92f7f17461014c0f40c57264135f954e14aee14d53815333f2f97d0bdd385d78f09eb14e5516384507431a1867c81a7075881b40ba7113676372fd7dd31a230720e2d41e63fb4e6950d6282f5a46485a7cbf6f4428001f06fb505021bb611f148d8cff5f08437376dd82a55a7c134973725a945e39ae750f1b340a75fdace4689e13c11488369d0c33a53c397ef8d52dde26cc19f57d845e943f6e12a64ffa5ddea6785474bcf33935123d5d095cae5f7e35932a17866e2608886c5e299b2c542e296849d0f1db42f77a5c13638a380376512a568069081512236a5a9bfe0b25648ac44d78f8066c8e393e09ae17af3122a8a433452bf73ac0c69a43541f8a104323a310ff31815c835e702f19991c502060ba26efbc1060d6e59c488ab31f06ba13587cdcac245c58020e19013bf16be3a60072cbe0781c5739f9649234843e96f321124c26eb795bbe7248e192e56f89af3401d3961e43d2f4c4047047743a677cde3f8d62d20dcbc4786d8cbb84192bcba344c81d5002e0d135255a73d41a21a72a3c4286ee1f4890553981de4b1f6d86161c40bc2b7acc63163e1af5ba31b38c92450243f052fce15262b9e1ad3074e05f4e79a1c97211f1d14cd3ecc8297a3a6115f5739979b58e811b27d3482749db6e7b600832651623e83b67a4ee6317440f2100c4df3c3e1e2f3e4bda7c2f7ec638018b253939c225b525f0657836bf7af071a50b0b7cf5010e47c6272c0e4604964910d0a34c9c5c7774cd19b82da9a13d22b0f4cc5a63fe1e41d4e38d6f7aa25d12f113e74e7cfcd028733e4f1fbbce14488538ca1fc2151a05c23b4356d4e6695cd6b8b4324c094505874119227b6cd91fe035150c0452aa0760b0f4233a9d785b9eb1e43a7ac1144b9ee681533aee563eac7cab720cc34672bad53535795bef59fdbe82607b1a0d2c0fb70f4c4fbee44cd1d4cb4b1dcb65176dcff42b7b7fbc49c17d1f698ecec7749744057e4a3597390e6c3f4f666a86104f6b1a2ff2da8606ce827144ae36d35465c5dc26a424701fc3624060a676221dc61f1279ef978b47235c1d0eb259c8758d495445c483e4543dee5947e3e8ca6b02893759870e094195798627f23fda6e4ccfa92df46a2c0b847ef13bef974d7ea4adf53ac3827c3843c9c3383e561e0b33e724620dccca55658d8b54e291695c2e2abd6c7358f37d6b7e52607c40515ea9d2aa3a61048d59b31df94a140c7803b871a75ff9529b2e5e5bd32de93d4048b61c4d56c79f78041de38a79f4af57417b2eff0a4a0db843890144319ff71e3fc9b84d6856b3fc1361a9471b915b2710b4b83b353029fe3ea195971ed824f92cd553a025c4098f3af37cfa7af738580802f702770d22dd63548a6770becdef0abb9ee02aa4da1806dc3245594f98f569f0ac63630f18671652882960c5fd82711984611ce0a9c4525ef7ee55158bb2553b535676c9808b7e4d1bb2223c4154034d1dae24fa00d63023d182331fe9515047a79b70694a4e1587bcfa2623699c046cf7b103b6d77266aa04eb4fc07ea255e3ab966ea7e9985e2e51120ea39f422eb979fc2789ae1c3eafbee154215d3b558d9ed774173d6e09b5057f3f44e06d49244b03518da35d4e9d0512327a798d1b927ea006fd0ba6555f42ee69a87ad00cb0afd87c2dea404a28eced02c25ec27a79e72808607f5022d154e970f3be05635f98e10b5859b62770aef0040f8d3953e3fa5361be78a31a50c98362ec14c966452ab82789d35c421223773408f85d27622b373844caf639c2972963b9076a10af82b75759e15e00f2a8da678a5b88376075833dac88b21e81621a73475ba536719198626bbf6b12b9a0465b83c9d5311e70fd73c35aa54a78801647d8774c34dbe9536db6734337e91e9e4ea0dc957735b43c4561ab1612c78a997288b90d3e3b3f400d17978450a7f20307c3edef006210874d2213f35e0e114f5f320481760b19ae7e8872f03ae7cd674867a9e11ec9d480551db4c554e1a2fa08335925757b5d161f95086f0cd628f714fc4f4c57f2930a3939f2716bf98a9d6585d9244fc436fa1da52ae01d9d77015175bc641d7116dc52c337a13d8ddc6f3f66174a0c335fcb1344ae0b1d293efa3c2b0ffe3c363d0024f3142a741a2fef23dac92e1a5590e55430cd5d6cb835bd3ca96faf7ef2ec1c06767c4e083deae30b5e96c0171336203f13c4d10058c1c95c4391306df980680f18d47079b7dbc01ecae07f2ba2bf9720f4c40a656da7c374f754894abb257c6f2908606019a2fc65bb251c61da85f454bf5f8e231db7307ebdbea03f64a90022612ee2575718de531e9c877c5656fa01e57e8f2e5af5c77990fd9b39910248199d883908de7d2f1b55b92d264dd893736dfc76223c4a6657fc99302371799524fc9f6d1834c887767480596ff276ab5d74880725c9c387768dc2a33056928f0e50cf88213fdffc75dbd35b53db8b6341c45e0d4771799321cfb96c3d9d72ab2ff8bbbb669d672a386c89a54081cf7e26015791240498752ce8875e40c6141f264f17a653028f962091eec14839d9d77e798f5313aa2c2121ae869f725ecbcc47b2482f23d9446a3e64f6933fc0313b2a65f53d640395285be771d224053edc4dd796077c0e79ec7d62c9f30848fca230e9a810743bee060ce7e4e13ccfd3ae484cdde23faf778317ad88843a738fc634c791fb2cf6dcb367798b0a5fd2cfd5604403da5457ce096d3af4593ef70bac5908b7663d851bef7ee06b7042a7b1b630965062332e59762bac095770c2a3367b25d0715438231e58e1a0ba258db5a30534e764077043cc3ccf8f7b6b325d287c3033ac17ab50c36416f0587b37970e6f3af35564827c2d779ecdd108318bb062a15db159f2f34e534288595527f4641a3d12465774f31a6bb8a1045f4835a9595fe5b4121bfd18731685e37a162a4f22a663bf4f0f59e51377c5b2123638fb3c3364744756025e01075bda36d4108f23bdfc3c52fca0647489fe607e870cec4d29aca3216d83474f9625cc727464c10018fcb5079d4731339ee1515c2ac4cf57448eff28037a72216abf05738b08f42b267e8b360e7072312a01901223e2eb14320bb96b6910744925449f7e4f354330672c512a75ecae2af427561210f64972450b45297c0347621e12632a3be0956789489458ebe540773dd56c4fcea5f41bf9be8875e6bfb56378f8cb47a884d24926a7ff4930c6084d9bb94d67a23a5e4f192ef225df60367ce4631850e0cbb72e8ca15d0789f42d17e1d0ec5860d3775b305886486fad9348736bdb439e0dd61566bd26283f36363d88ba3064f4d13551a121375647f4aa62700e712d5bcc9f09691b5e56b7e83c28b0e3bf3a2ef2b022b161d269a5511a108ba54047d7343109552c906cbd98760319a5e5074a0b6928a967641f5509627e1aca7733f751df135a4453086160f539fb2264678bac78542ee9191536c0575f02353d7e8d5d2d08e5aebf0ff92c2f19ec490957a235c04ea4e4380b22ca4c5a38b8064abd523e0277c64c66e93dbb6527b5072d2ba77a2f84b016448cfeb409074fcb2e7000fd09aabc0e4e2ca6f374ee328d36b399f9749539f6433e5ac268622a9c418e9f684c43df0b162c90ec6faa3cb81ef5ab856abc2ce803ab4ba039b0213802bc097a4bbe05043d578b026ccda40d4cde5cc2741f59730758280f057e786c3b940e705bd4b7d65be1075155938bd7244d5f7220c177467910718d01bdf1d96e30728b6e54e3571b9545bd19ce23e55f4270dc78d12b352750207f4c660d4b5600bc372eb26fdd326f762a7280585939585a484b0ff2f34f081cd6207ed5c91b8918c814ed94570884247f3cd2e91677b0564376c5f5e5748431745aaf97e838697d5039dfaec513eb8db05a84f4c80fb4332913c848a04b11d21d2ff5942f2385419749f6d273405d91bd2863c0b97e7f28f85f7b894a1b1356ca715e8f29050a33fd2556ee6c2f4ce4f6166fedc60129e9467572fbaa1e9fda5d39e40c31110c3ac5532668034fadff6157c95d491c03ea6f03e662a20b8792741d2d887c2483759e17c54cc4569814e802271ee25ae5d84069e295ac7e3657452653dade1f24451109b3dd3a56deff596fc9af8826bd1845331edbca7daac2cd4a42840a06c8c013565cc5ee1a6484ea4e150391707221db1342682c3ee27f712a223470744ec4765a43e1027e346215430301666467df17130637a27b989a5154b0f73867a2b4917441a7745de24dd441dbc8137067137b7bc1ec4917411b5b257b4f77019f430f59c9b823646501bd4d94ed3e0a1380ba053c95440c90429a0b3567e62b13c6b86a2b27515006ff8c2b7c66a52b777eb360e525ec7411cedf1f58f77e3d8467f03a699db0311636ca532c82961a703d545da8e5733d752aa36c519fee0a54d2820ff9ee94067fd329181ec1b06956855359a4ae604a8424891c4b44a247612f0b4ac7677d15a0fd7739c9ebe07334790305eb8915601cc07e6c71688c4cc369fb0b264f4f0b5b4e046fed1aed04433ed7535d3c6347e045e6734a4a6a31cebd2a75c8a7f770ac1fc17cb399f75c6028ff062f500838fedd427e25f0b77a9252926554856f5eb132000e81994944b59323497cdcb55323b3bd7a67668c5df77f4963bff516345066e0639be1ad2e1edfd61fbf168548d20e717c388e31431f63156e3a2e2e5f684c0a42f6474441c9c94a5beda758703aa208053145932e5e21061dbd13236b89889a7d9cc188320659376ad167747ed8e239009c7756343e72d13e0bc56a6847fe73554b0de87c31d8d70e96f9165e7f9c7a583630d87b11fd6028fa29f27212ed4d18792a2b392929235ab2e2944715249b7df0be4d5e3cb4e540a0a62249f202950715249b7df0be4d5e3cb4e540a0a62249350f68499fcab759776e6e09e5db277b0d9148490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466ca4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466ca4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466c3dfedc453d16e14725e4cf64569a502e0fa8421521b2556a086ff867aef8b019b411d02ae76b9d30ed745221dd9f5b46d9f1ca4dee6efa520802db5e4b9ba13e78a6c270a77385647e18eb31d57737632205fa42b6bf6703769e1407f4683e39e35b2f15f6d4d62ef69d631446f35f1fa3f3583feb45630555a9b700137a7533caa4e757abbc4a64d1bab05dbcec1159fd0c337d45f2ee1af52a976527016d3e65275f6de5568c12b01aee07eef89522998f3635aad95c50f0606a73d9cb7a0025a6f42864748b053c7f3043b3c2fd4b91035c4b1d62c82db04d9469a3bbf361f817a2038838f80f6e17a7776e7dba622734932bb5dac759c45f83230bbf5c6a926a827b5a7d8515e640816a22ee7b296922f760b55041663b4a494c3245b16f9f19772768cbcd1cd3377f39e6baff3999a23b0fa0e6e046fdc6025adb214364c7632f4570807a144121a8769ae8fe1711e6532e8e50ce4441f9122d507c283c0013053b28b80945c838b55b07d6b0268ff3420daee7f13ebe27d40db232ff337b1f1c57aa3f03456a16250fc2a35d4ae93b6260675c0b2c23987576cf975660671715603ec19a54a5907c7e16e0cf6a0b5a3c1fe453aa381b72b9701154ef46966c510748307f1eb9e9534e3a6647363d513b112181a84b3bd99b0966788f1c33c65d0b4b3cb904f42c911ff4ffff4bda71a53329ef4c0818008c5e5b417f1ee5fa717bdef15159a532b16d000000005819bf5d0875f9098f397672a51c773e8ba8f523e9755d2b4d51a25500000000a9a6bd5e75f3585bba7d015417c7ac746f90de7a000000000000000000000000b5252a0a5dcb911d18de6f3a1c2d531de754da7d00000000000000000000000096215622a5f134011d8ceb5062e0700f547a716d000000000000000000000000b94e21612bcce355043d183526174a055bb3a610000000000000000000000000e31cb80aea30587b508ed85bd3177f39452e4a570000000000000000000000006347801c32991335df25192a1c8b125fb43d4f41000000000000000000000000539fc537ff6c887dd5ec6b58e1d4c77e3cb97227000000000000000000000000e022631ee43c3e18f622614b48887d194c86855c000000000000000000000000a005f653b2ff693ebdbe49022b09473427b7d559ae8173406b5b690bac1630403b9a3b068bcb2404247f67065fbd765bfd1075407698f35c5683f73b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa214e5c8159f75f02de7d0174f9bd350431367d33a6900f33b3796700000000220cfe36a4cbed5690c46b7214a2307d18c96048222f7419d148872200000000c9c7b439d54854699756167c4934e3208c6c15390000000000000000000000008aecdf27d32729696f6625032bd5b310dd097d04000000000000000000000000af65bd01be9a717e9dd66a7c3e462840bc0b6c1400000000000000000000000026448a789ddadf441157822dd75d835ab2c8de48000000000000000000000000fe3c3a36ff232f5f891ff417ac51995a503c4339000000000000000000000000cfe0e426deda0f6d94d48b6720189e3babdeb11a000000000000000000000000ec54c04459c46863d7db7b455624d64057ea5a6e0000000000000000000000006607d8106403782d7eac757d77d7235c038b5504000000000000000000000000023be52771763825088c202e382c3622528f6e38282c5150402d8f4bd5bd4f3b14175256ff90d60c0110ae26921e0103a7cd1f797bb28543fe2c777d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7daff24e0b28f430b483e12c0c3d551e94b672b9a573263f46eb35300000000cf093819e0e8a8063c6d675e0e61542a1448e24744763e53d757a31b0000000095a1e66a38a2ca3be2bcea26b030034a8ebf68390000000000000000000000004c2bb6666edf731165cc2c5d4577e43816a27677000000000000000000000000e45fb6499544883245bb2e0d124edd5334394f4600000000000000000000000049d3866245f5b31807767c3f9664a06ace6ae65600000000000000000000000077756208a926703bc3bfe93ccb442f31248201600000000000000000000000003dc1c2033fcfad66d19a06052980f34b55ed796c0000000000000000000000008b401e24aac0fa122a7b7f45b1f73b09ee2c2641000000000000000000000000df817a08573cd66defcf5900071fa40902336b5100000000000000000000000059e12b69d85af0209f675c507e538e12a952a857667e3f39c695dd7ca54fc566fce5cc1eaa7ca955f1ded20fb3264e4d195e8e5dbade6d094304752e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083419c321a47e8753673923ea50666301a61af607310850c4bca230b0000000076b2643babe3083ee0092f08de35f856f509d03adc1cdf5a45c8323900000000c3a910323624e416180be54f37cf284f742d936c000000000000000000000000aeb9c85deb934a28f03a0d67d5db692066aa600c0000000000000000000000009946ff033e2500220c1ebd25528c8532022f3d05000000000000000000000000e975227e229ee306d1d2b34d4f173c0a152369300000000000000000000000003b56ca449d78ac0d626260652f8cd62f82b4d7350000000000000000000000007aa78e0cd7f6e46aa063c974b931f3418d079f430000000000000000000000007823f74a3f594b13d0f1b96073951540f8604539000000000000000000000000a3a6272f3210e333544c836edf06f76ed40fb6300000000000000000000000005c3c642b9600c1706ed6033bda20ff622f295b67be9c7a61fbfab94100e1dc5cb2bbfe365bc85f33c511717d741a817023d0aa3df7ef803ed3798f090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006789c35a532be5459f20a82b40f3e72ddb1ee229ab4aa570ff72c41d00000000c8bb252123f42d720d474a4ae8665a6bc6fee01f3de72f47e7d82c7d000000006b8c9e47e9ef4802d4993212cf7a197b02443367000000000000000000000000d762f13daced2e6345147c7823f3db1a18fd3a0000000000000000000000000058159d582ce4930d26b6c03bd78ced509e38f5710000000000000000000000007c91f0756162e4141497570b8fbce42e33ecd10900000000000000000000000036386f0e45a2e705c76eee754f0d1414b3d1fc3e000000000000000000000000d350334c0cd6a84a8a1e3810b36fb2007b67903f0000000000000000000000007bbce82ca471215fd16a167aa37597344a744863000000000000000000000000dcdc6965c60ba323a2a7cb0aa9b6ba5e8aabfc64000000000000000000000000c3431a06a6243c51c6a979638512951ef70a4165092b8d6531369c3b55219c18f1276d333a1e7773889f6719521ea464eb81ca75d186d84654ab830f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033f1d78c63ea771729fb7434a1d701bdc1e9228d8fa572bc57dc257000000003bbd394d7d56b52214667855dffdd26c576c0855c0ae5e0ef0d8581900000000dbd95229b2a59b2450d2a87c31c93d7c0f0ce045000000000000000000000000392d44226683a82205a9d1095c40c619b81f8d62000000000000000000000000ec38b7300500613944b70d3c965c6613f114ee34000000000000000000000000337db47ceba0120fb0ec477366a1514d4909af3f00000000000000000000000052a53e1566b2af0ae89047572a8fa0548a29585c00000000000000000000000007ec7f407915632dd1a66537c4673410a511b7420000000000000000000000002d0ad52dcabf45272c51453243866871c1f9e619000000000000000000000000f079eb42e17cde642bc2c519e7b1db3f546bc4010000000000000000000000000a70b22ca63d47632f5c517c233c0b228addc610879c065b95b0b97e9a81ac3be87e0c695cb9144e55fbd756f70e40748f436d02dbebb76fa8e7602d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405d8f3bc2ea146629b14d154a4978734c5459269627894bd6c7260400000000b847cd17c14e9f1af6899562c114953f6bc3185e0ee6ba6b225e647a000000002219621ac1b2e00d575408077d5549506d539501000000000000000000000000ddf2494641a5e5314e03625d57f0a5127888e8610000000000000000000000008b0e264cf4439e17bcb9810480901b516a15516c00000000000000000000000006399313c956f85a3d5dd31d37429b744453ce6f0000000000000000000000006be0e6627204581209807362f10ca652ff04ac3500000000000000000000000036890950c1596e743ed5935f50958d3a6a46110b000000000000000000000000aa47301ca5b2ea5b1d11b1507714a50c15cbe615000000000000000000000000b51a486ac4110761c86cb96cca0c0d310a03f35c000000000000000000000000327fb076c206c128590d9071639cf10b80ef8f2d4f131c267a66941058611e6cd3610d0ba1e04a74cf644872e5b10f3037cf244cedea850667a2f67d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c742132007dc8776b7c17a4fbfd9542dffdd9e3096b77b52442eb44c00000000db85d636ee4094335ec16541fdceaa587fa22a2d8ce0782ae5a3733100000000be85dd4954793e593e7ad32ff33dce01b73ed4010000000000000000000000001b4bd56f839ef36a7bddef74bef6f47450bae65b0000000000000000000000008739965b12ab1c534248974ca0beb8057fad54030000000000000000000000002290615a5e11595d29372966811a9c1d2a2b3c210000000000000000000000000b2e381e88054010f0e0333983ebd12eee1e7e63000000000000000000000000c3d7975ca2e160581e4f8a4a8dc1b74058ca6e3e000000000000000000000000625c2456f71f5c02ca577b43f8ba2050fa4b343d0000000000000000000000007456d145c7add51cc7aec2140bc8b9166cbd08030000000000000000000000004fdc112f588783726e5f5a4a1a59977292881307f12f004d86042c595b695916325f4625b996bf169551b72304d59b2445dc3922f6c0096e8ffd1d39000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa4c13321ce5e374f3726a27eb9c5e7476343d768584492759585e1100000000e3de212c2c725b328d1c001aff7f65721cacc62ef0cb2532aa2b5c6e00000000ac403a53cf9aac11e8dcba1924d93e551faecf51000000000000000000000000b162312f8635de491a19b542b4bd707dfb3d152900000000000000000000000083faf860ce321901b6ea4237cde3d06e9f559746000000000000000000000000d9b3f063795d970d2342837d205e8733625550730000000000000000000000006a497f3701d1296a584e4935eebe8d268205d55a000000000000000000000000b04c9b6f34298b32b808f2144420750d1908464a000000000000000000000000ca3b0d37f03a2a1b8c551e7a8edbe55522b394630000000000000000000000006079f00888dac336c0009369964eee37fc7f7c7b0000000000000000000000009542f94cbda0782646006e5ba34a366565ebb16af22b1a157186b67d16fd803f05efa54ef4d36b67c3a51435051e1c2a3edf1524b7ab8e2a244e5b07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba6419623ff6582b7153d85f2c113f2717526b505c753c6a357da665000000001c9f861ec9d6ee2296ccee0c2113fb29c8dd9a3e523c0b5df54a4e70000000002979152155f954121ca3b73071e6dc7356add74e000000000000000000000000ee22da2e3f155c4dfa297958ea9a07320b3c617c0000000000000000000000007618b048a6779c407e19b17b6f3b941c5e0645450000000000000000000000003253ec6e8544ba7954bd1f50f5ad1e5bbf79bb7d0000000000000000000000000cb6a25e10f18252e7e2bb4919486a418fb3b4510000000000000000000000006a3e8d65edfa3e64fc82d86fc466487283a1f07e00000000000000000000000017a7d1658791952a2b770965fa1dbe3cad66e739000000000000000000000000ccf161093ee5217454374a285b66860bb7ff4a240000000000000000000000006beca623d93ff61879a86320ca39947bbd31d0099943a61abf2935439b073451fb08c85e8bd1223ec80ef36d200caf01f1348906372ea22f298966300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a9a415391bf613d9621c1227454862a6a69a9172b1b5c4de0337e75000000002ef43c479e0d3c08b7b9c351ab24a2582fd3ca57049d27403ebafb05000000001885b246d679820b93acd20210477a31d56f110b00000000000000000000000054994d503de96422fe653519ebdc97368a83120d0000000000000000000000001817a24f50ec800069ea76317e7ffd282fc46336000000000000000000000000b143c96da2ced04a6b75740bc9ee3f1047e8a20600000000000000000000000072d292318cae6b6de4dd554702859732c964e1710000000000000000000000005a132c361856c439661100791294856c11857e67000000000000000000000000dab2ab369e77183a13d8df1c5467fd17217f7144000000000000000000000000031c3d6ef670f11d2f39500de7d0466fd72948510000000000000000000000009b7580259c902735e7d4e70f22d78f01c768306b4d0f451f2f4d471fb6b0a41dcc857d7d2a5a6f5902eb9b72cc1625497545521054664c464350156a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b07b3e5aea585f127fd5aa2ac100aa08bd143b39abe4f94ec9f0143f000000009823507e6c5f137019110c7a271bf434277deb52881c095f023c614d00000000bd1c507a769eeb0f6f43622a4b79113202da2c17000000000000000000000000731ab534522cfd0a22242045d836295d4f30730000000000000000000000000006e3c778ad20803f3c1357107aa36429e62dc15f00000000000000000000000083438323fe7dc43c52240c42dab0924830e482610000000000000000000000004873f01ed8ca2b69afa64a289341eb76fcbd2a7600000000000000000000000085817729002a352c77c979592b511713a938c849000000000000000000000000dc0b97266252f74a23358c260122840dab80433d000000000000000000000000688a92139081ef361fdb6372f9746d51f5472d07000000000000000000000000d62b5a1545ee0413d6ef6306804773322420811dd855514b58cd0256701a4e074896c113bf29582406264362dcdfa26f1bcd501bc6790860bb63611d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a764f798688da6fd54b950457ef5e760f439072b1e35a63e8bfcc08000000008ce21f645d8b5d760451d94f1977c22f2c73cd70b82de46b5957a6490000000072cb1b512a69440ad47f26276735f9565d3e141b00000000000000000000000064a3a867eeee157a3583fb041b1e36311ee26d2b0000000000000000000000009b0c611b81031f7cb4b52c3e451f300ed5691551000000000000000000000000bf1a1560c8546b08f17927619d9d4a0bfb75e10a0000000000000000000000001e33db3ef42c3439e028ae2c4688b3708731be010000000000000000000000004ce0dd477274ea72cb7a57644fcc326efa35e96f000000000000000000000000c8f3717964eb0336e2f26c4048548c65d168017d00000000000000000000000061b47640dafbf31f19ec75366a4a994c4109f115000000000000000000000000aa3630391f014624dc697e2e8f5d6d53fa64770d17eab7054cf3e627e0344c512b97431de995d4769073805ffe072e2a9d20587e24a3d60f405dd3720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4e60398d02bb31ca553d160adb4155828f315876188511fcefc05c0000000039abdb5ffa559b56d27aa476612e822c221d671fe5074745595b241700000000bd1e0a46a406531716e0fb676cb5f0407e925a09000000000000000000000000691fc801ef2b965fb9680357a003a24b6b88cf49000000000000000000000000f50d4c264a4963622bb0646db5c4673ffcb29119000000000000000000000000dee36e637c9135448d31b36253c0b0309a3fd641000000000000000000000000bf314d3db21e4f412cf440222042654ae08dd30f000000000000000000000000fc0ba10b63c74c44f1df621545a82b2b4339cf3b00000000000000000000000028dcb6099e57fa74e9272454eaf9b173fcf862510000000000000000000000006682533b2ac1e03f4fba1a157752ce4fff9db740000000000000000000000000220d59594cddf612f67ff66ff93eda3c939dc07800cc3f612b31f474279afa1456a4776e446c2b57d503766991d26751a7bbbb56b52b2b030e3b987100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032d85473c0c8ad27bef7187bdd72741537dde823ef72a42dd189c930000000004523e17cb1bd453313a22e0aadb062382a850449e26f781e18b94b7e00000000f1d6336a39e5255c72cbb56612d2b71cef290c5c00000000000000000000000026084c216b137d2193842862879c6015ba94df62000000000000000000000000c9f0a876bce50832e3a2ab26a4694b03439c97400000000000000000000000007ae77a06b1078872c8e7b3767980474eb13dfd0a0000000000000000000000001c0f674119ddc30fb17b307cae80e73aa54c423e00000000000000000000000011d2f47690dbc931a184207071d49b49b62d021100000000000000000000000053937c04409d0701c8a8ae16fcc7fe1665ced67a000000000000000000000000c324206e4973a00f95f4f33dead8762eaf75d824000000000000000000000000e5bdde35e43a0016d1bfcd59ab89ee4b551fa779f2895e546b4cf67c864fdc5c8e078b6ad174e23097d51161eb0da91897399c1b1c55fd450459df3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8073938a6d4c630ea02974cee38b32a5c2eda3470119e6478c93b3500000000770d944c2b7428708d4dcb5d8a04976188ba9d5fa4106a0ffdc2f022000000007caf937b60c8c9620a43ab444847a079642740530000000000000000000000003262897c1627bd7807735e4560196f34e9f094740000000000000000000000004f34c319def40829ca70442b9ec3422b2219013e000000000000000000000000ee25d93be9e47d657699a40e7a319b6c8ab46f0d0000000000000000000000005cbb8f67617e0d2810a67e3553bf0617d85fad170000000000000000000000007055c04526bc12797cc0f01dc08d7d0dbdeb694900000000000000000000000081166956ca10cb6d83991d0569d96f0a06799827000000000000000000000000ddac8d46580d7b418a8d8403525f3a59d9935213000000000000000000000000ad0de767eeb4653616c4692a92fad27e5ab287553d645d3ea9c4171a214e92219c597f2b6470a733cceb0569606f4069924dac2f8500d338cae6f4020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008411e3465ed487132a09617ef1efba2f27638f5093b43d0746bd4178000000005e9417395f592a52bbee034520631f2b97f43462aff1480aed860c780000000074684673df79da043c2f003b14938d0a59fc87280000000000000000000000001ac3395a78e56b6fe6dffb61519b8b06b5406d1900000000000000000000000083d39427033b3659c10e4101cde2692577615c2d0000000000000000000000008004aa0d0f858d38726f197d0df4ac1b7634af5b0000000000000000000000003e4c801278c71e77ec0e2c5b296baa0c46752f2a000000000000000000000000aa7e9b08741a160ed3caab2e5060512877ab550f0000000000000000000000001b3fb961c946b709218f4045bd0f1c48aff801680000000000000000000000003cbc0a23cd88000ad9fefe36c148965106de8b2300000000000000000000000061bc5711a84b5e273f9a1576bc342e3f4f7a5b24055ef46923628f04114cef6d5532762a8b7b5f2af06a6817890dd04a361bf0519b164c269221d72800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002740870f667c23588c59a172807365c0a7eb22264e40c14ddf0202b000000004a4f1277a70d8d718486806fd82efb69d45719191a585c0cecc7f173000000007d7a10661a261220d74e322109f76162751b6327000000000000000000000000441e633ec0fdca6e177b2f3a0c75d06f54d6603300000000000000000000000047a1764d61ee1e35a410aa2b22480670c606f345000000000000000000000000a8c7d947e4e49b25be045822fe35b6785b2816690000000000000000000000009cb47c74b6c32350de0e38000c47720361916746000000000000000000000000f768aa020f52ca0639082f724d334e52304f9f28000000000000000000000000dcb29d73c7724127e4bcfc625275ac597d945b1b000000000000000000000000da47893865233f0ef87ccf54e775592cd5bd6d11000000000000000000000000b2297054cd16780f3e5a33200c2dad3b3657003576274e41a5191c172ea5643f27145e716252c400cf6f156f8a8bc460f9c4f1618c45d740bc0b8977000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa4a387617a7fb7e9d94b92ad33c971de88f8e13c0ddd3156ccfc04900000000ec0ca34d2061eb4064a7501e206c425aabce7a3ee6a764324688062100000000fc9cfb3b0de7bd29a756691516d7663f4b5bbd4f000000000000000000000000a1d9d65b7a7c602016f8ec75c2fb317702654e73000000000000000000000000bea75171f3a0bc28a5e5394034e9a91a2952b80c00000000000000000000000001934d08c2cd100a312a7243862d164cb4db362300000000000000000000000090cb805b33d2302ea0bda86edd39b346cc701054000000000000000000000000f887923e1aa65d5e7dd7f533a013102167ea7c46000000000000000000000000e7f37424b671db106330ca5d2b2ef17a38c51b360000000000000000000000008e351d3aff970e14a47cdc6edf755b58ddfb363b0000000000000000000000000238064afd41b512f65c96149f83c0316919d6726678df0404543040aff01c57a7c011594177362c7992942500f55f317083321df4f14560ebd3e05e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e3b25c794bcc9721e18c4854d9cbed2897f04c7dda25d52c13a8cc0e000000005263f56be50cd65614be235112b76b5e04886512a9acd0123ee1db730000000023ec6f168589dc129477916503298103f50915040000000000000000000000005e63ee5713f67671c4cce859d1f61228926866710000000000000000000000007c4457069d2fae101b0ad96257f6f10f087dff68000000000000000000000000ad23c20b01a0e3759e616807765f427aab624959000000000000000000000000bdfc9d23ecf87d506278986b3b45f5607588b7060000000000000000000000002280c11345ca6e266102995018e7a25ef984c568000000000000000000000000ebdd826e66d9816305f02148369bd76baa20f82e0000000000000000000000002e41c767b5c48a2c4bde3f12fa893c01440ac95c00000000000000000000000044641c20f5e543087af2b07e5be7af08f4a6bc77983d6663c5423922b0ce416cc199e27a17bd8f70c346c4218959c47dbb83b35dcf34a2523c0945510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d57665bb45ea6152a9ab718fefdc01a7b56665c0b65004b66b3af4b000000002bbb006e75122779aa732e7e52a99d3b1ce4ce702008892dd8f09e4c00000000e16b156ffdc6083c0e9e6f4ac58b54787e5dc16c00000000000000000000000050084e2d8942997626416f70dc2664089dd5e370000000000000000000000000917a40788410881f327cf526a632d133430a4a51000000000000000000000000acf2be68c9447448662c34396bc9e70ca5894f74000000000000000000000000c729fd65cebddf73c183d1757088bd56b110f450000000000000000000000000b69aae295392294893af0a5c3daaf26933dd6b54000000000000000000000000a759463fc98a941d0103df1139654c2b24449e7c000000000000000000000000f6b92633f2c0bb4107890f614b24f003f9fb402a000000000000000000000000c19bea3feabe1b3e10e49a3cc7a2ea636737ef1ddf5bd851745c926689c7ef66ef9422704cb2cd3f56a6fe633fedd0718e465d569af8a9374e79cc410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a75fa4115daac526ea1a5110ee63c427209066c35ea0812764edd1300000000c7a3b86ab9f2401565521b30a777567ee2408b106b14204ecda1c15300000000d1f3da5badb75828905ee0672e4c2e4c8595045200000000000000000000000055519d0fb505977e1e033a7d291b7f5cc806030d00000000000000000000000071941227434872265f9ced239e3d677b468d0d110000000000000000000000004bd3353525c18a42b7703c24ad0e8a0a6344dc07000000000000000000000000c009943c68c52570cd430b3c242de03a9b5b44550000000000000000000000000021f0120f2cd45b77e08d139ee9935a00d52e77000000000000000000000000b12a001a8274b075a384a752197c60439353c11b00000000000000000000000067ba1019ce2d9d1342832a114dde9f258611897b00000000000000000000000020b5240b197ff95b38c8944a176de50180de5f261f5390536b733d11d8c0a42162cd8449b2bb1c04f4357e14e230fa058ca20b25d64da47eca12ac14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f237823fe9aa592d40615206a8d137419b1ed926ebf44246fdeb7b5800000000a65e5a15165b63451f0c8e5e95674d1359d7ef225b8da077c8fac71100000000fe3800541be51e2ee66ac724dc13965d33a3210a0000000000000000000000009443c07da089ef5ce930f9309d4fb07eb305307e000000000000000000000000b32b4061a8db2669096b52550cf66f7c357cbb480000000000000000000000002425f8701b84b05ec14cb340dbd0d379859a1844000000000000000000000000b1688708eea5514389fa356192a69e265437a8570000000000000000000000002b95012e6ff1b744284baa0de7621407863cf8450000000000000000000000000d0b044dc7a3591d7b829204343ac824f2d71b240000000000000000000000000c0b7a099699203d68d14e5ce63dc7681efd482d000000000000000000000000bd6dbb46e7067a20c855756ec441564411d5291a85c77252e8682228425d672779ba5646b27cb9360da2fb52518f1d3e710b91778f4fb07c8d95351d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a242d0fc819f01593c00a59bac1ea7c78f2fd12704bf24f98ee03600000000085d15765ec038d31be49b00a7df21a625b0345043fbe776282fb982400000000f88f8d74c6dfdc12dd1dec7b10c393097cbf7743000000000000000000000000f45b917744fdd67286ce4514f1ee33485ed03012000000000000000000000000cb3c4140dd25a033c556b3304a8dcf39d85e836100000000000000000000000033b65b1fd3b511452e1c364ed0c7347b7a0c6b5b000000000000000000000000371df645f772664a2dab40405f44953136e3a42b0000000000000000000000006d36ce64e41d894f7cf3245036c917170c49dc0100000000000000000000000066f77f7676684d5a392dd877cebf4a412f084d430000000000000000000000002eca213093e07750bba7a34b0f02396b99bcf966000000000000000000000000ccba353e7bb57b3ab8464d3dd233aa64191f7c67a2921d6f18b7ce357317b86e92d2ae6eac35d50c39b4c9750dcb91129728ba5cce85802c5ea44c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048be2b75714f6139d001d734cb608f1d842e694e791fae5fb3b86c2500000000b5912c761b7e203c231db34d43b1a44a1ab2e53656ba7d138561935700000000f019492a5f64304d4f5daf510e41313bb24044600000000000000000000000008520b00b87894615d7d3195348f6dd103e624e450000000000000000000000001250cd583c217a7279b5e63b24fd193f319c6b0c000000000000000000000000ede5a30cff2e06721ab0a14d46e7b2140ff26d78000000000000000000000000a879cd27782f5d3b03a0f019d8e8d220853c2911000000000000000000000000b8ccfc553831747d4a04bc1a9c619157b0aca63c000000000000000000000000c253ba5f368b3433fa160e02eba55e3c21521b6a000000000000000000000000800b215eb5acee75a404532276f7a86ae6e37077000000000000000000000000e6a87e1e61a4e3362e477b39a90e5741db3e8b3c8ce3660d5c067219c92e7a6b82d6621b06160565afa3763e3b41a0059dc35d158fd4e12361265730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000648e6c13901e63343e69eb3c340e5b0652650e39a90c736af941545c000000005de9c71db3e1761a9f72b17d38b1335b07a62a3009de4b7642178b7200000000c8206228c8ef652d302f405ee942126c44e7f42b000000000000000000000000ab9df1615753586f4fdd301659ba030aae8cd05a000000000000000000000000b6ccae40c9bf317d8bc1f60e6c0a1f351f4b661900000000000000000000000034e3ff17b0341f2cadaf092e850e983ec73413550000000000000000000000002cd9ea79fbde0a1d8d892459d19f736892ce140e00000000000000000000000074963105911e3b34b4953f4875b2d92267e01f4900000000000000000000000027f760621d9b156724cd6c605f28ba0361d182700000000000000000000000008542c36046b7f058ae2cb311fa467d5a05668856000000000000000000000000c1481e4c57fc695aef5f5c5c06c95261ba0d627a078f0534fdd4e975580abc5d3dbcb6512bb8c028cd25b31b5508f256f3702314d6b29d780d8c0267000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dece435fa82961501cc8c96906a8ee1a88b3f616fdb86f1a2fe30e27000000003383802155be596b90dc3a2abfaa707048235a60b43dd54f6969e60f00000000fbde552e95e194601b4bb410088bd57ce5a75852000000000000000000000000c5668524c5a84d693f78111daebebc4abab93561000000000000000000000000e2ae8f5351cbc361a3c7bb3dd80f8146d6df6b54000000000000000000000000ab8a2a50a0931403eed5bd0c852c4a2d7402403b0000000000000000000000003ea7055c5c6a794c59d1b1534f94a42276f97a0400000000000000000000000050022204a286b302673c7100ec309151fc52ba57000000000000000000000000306efd3966e8eb3534aad93a359f4230bd1287250000000000000000000000001a91b82b1817911a15ea603653f62747fa795e4000000000000000000000000094aca0080ed0cc416ab43f43669e636581d39f58b6f0fe1b6d2c87689e34e15588f65b0771f8b63b44d8f72f94bd220adb1d4d4f1fbac66ff3431455000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6dedf245e0c862f0810b3426c4312705058602e403ee13e1dc88c2d0000000063ad85747f65c161ebafc915a68aef71b37e013e9209fe4294fbc13100000000acd7316cea709e705e43756a7daf5367ed7d9f0300000000000000000000000031952970bac84138fd5e9b42c1ff5a17b3491d2c000000000000000000000000f2fbd17c1ac69352731cee4d08bf953c44afaa3d000000000000000000000000cc59b60bc5c7544e4ba3c518699f9a092aa09a4a000000000000000000000000c1092d51ee119933297c85094238dc7ac842c030000000000000000000000000bd37577c7123f72eaef6e43948e8e7737eea694d000000000000000000000000ed93b47ca71e0a279349c93a3e810c5d307b7073000000000000000000000000e8b82a371d176e414f57e43f27ff5e59e9319966000000000000000000000000eef03d51ba8adc478dd0203eb1194a5fdaa6cc438f23505f707d2556eaecbe10ccbccd7a75be272b5496de499cecbc1ac3c80566d0e8b25cf6700d5b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d86ed6a47277e4e99a7ac01b3a93d6501a63902191c1f37f4dd44120000000061bc3b4095189b310e3ff22cbb73c1715d7c2e62c1204a4dce60197a000000002f82eb78b16f0a1ebf24c06b0a3d3d06bc5d5e3000000000000000000000000001fed9347739296f7343151c9233ea3dbabd506f00000000000000000000000064ae7026e510c7070ccf8b5201b65d6a06478e1a0000000000000000000000009130263b16925535dac412473e8ed5786f41d92500000000000000000000000048eff836f945d850e3983225286f4b45f622b7410000000000000000000000002334d827f340c2454df98858cd41146e02392b17000000000000000000000000cb0cad7ec1b1c12f4f81536c2152c478ff4d98400000000000000000000000007711506648f6ce35b0534050666e8d154b57606500000000000000000000000064b46e1253604e38ca90861c2f0ba501b2f651620138e740caf1db6a6e0bfb755f60af5244d8db03f5ab841d13a6f318f551bb333debc0124c1601580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b47f87ca5f8102bbfafba3ceeba645d42912846c5461f6feb85f5360000000090954c4578c6e437d41e6b293e398f3ea2ad800f3a49f95da0e2e9730000000010f5a930908b6f558f935d659b5dea6748b6f77900000000000000000000000036b833161d5010388d25766410fd617eefcfaf7c0000000000000000000000006b58e93033d9d7667cd15c7b2302ed387092a5240000000000000000000000000d06c2683e48350526dc8c57a65bbb4187c5f23d0000000000000000000000000a693942d435386fefc4df098dcb24293854a9270000000000000000000000009fc4694473072951d32081543c50d4501bbd96560000000000000000000000003dca1a1682dfb672451f872fed7c7973f4f7f5690000000000000000000000005da2273721d1ed1ecef7315f1baa2e7190bd4a740000000000000000000000008f39814814db995b3dc7441be20c1464da5ceb60e6af161c4e3f7204a3c941412f685c0709facc09372c13234814716bac98a8067d66d34349678a1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b7de9692113171bd0da9000aa0ac1723f9c80742a961204cce6057e00000000bbc3a75eb952dc290d54b239672e3b56657451191c136d38b4ad270f00000000692a7669a82cb9595e89d35df6d49372de81a16d0000000000000000000000008d70043cf27419228558f267025a914d8cce33560000000000000000000000009545cc008add5e39272f93588ee19b38a3a0de08000000000000000000000000fa6b6b12429c6b6acc7b0a6dfe77240d5df9a225000000000000000000000000e0fb770e8c3d1859c1860d0131e74a770444741b000000000000000000000000147cb2262e7fb61c6b7b3e2ac6da7a30844b0e62000000000000000000000000e8625c6015a48f34b6fbc3017e3f5d3f3d05f26a000000000000000000000000b4d7ec3411174870a2bba25451b33556d903d151000000000000000000000000ccd975123b083c7c7a30f9670efa72691e7fc31dabe5f84b8ebf422ebc14c21afb21800e7aa58c2636226604a6981c14c784012dac9f5b3dca74063a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009f67d7063833ab74ee39894ca035bd767b3f7e73e6ba8618b7a48c3500000000d4426611a0d7a936eda6a65588271151098f5d6551987c365158e03000000000071eb65e892e661ecf09b93f1ec59e52a4bae44c000000000000000000000000cb68887175bb243eb416a879bbdd355f23ee7d760000000000000000000000002ac3286df8c4fa37e9118068175a3a621caec666000000000000000000000000cfbb3176c982af47bfff3d7ed766037d213cad25000000000000000000000000449d3b22176e0d57a0a8e462e5f4cc40061cb76e000000000000000000000000d5b5db3393b6492d582a00030cc6f3185d6b4275000000000000000000000000bdc23100a3b0124e82584206e9d03c2e2ca2290b000000000000000000000000a0546502d049747ccdfbdd357758fa262d97c52700000000000000000000000013a1dc3d7e5e7b29e3b84c498d30de40e492f2067921df3d8297d84a1382d548181b6758092f7918d0bf513a46ad164ebe49f035c722a239f8e18e53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f840f37d3412cb232753a70ffe28890c86696c6fb6fb850f123bd73700000000973d4b2ee83ae7644a794365277972775095ab507e084a19917b295c00000000ae22374ef0cc945e1b799e6a41e99169a161f052000000000000000000000000d9248c69aab5077367befa6ce658ff4ab56e3b5c000000000000000000000000b940846b1d7a6878912d8e15a5b67d1bc4825158000000000000000000000000ffa9f22ac9bdcb0da7ea63108bac601b0574db240000000000000000000000004b9de979967ecd5a579d7765b401fd725f4f945b0000000000000000000000008bed416333e74c61317f7839579c8d2bc890f8550000000000000000000000002cf5cb3fa1a9fe38e7ec886b3361d5317ae5dd29000000000000000000000000679199482ff8e606597da61e22d52b45c9195f270000000000000000000000002d267a4608d7d0611d23f27bc1efe13fa8d7bf00604b0634578b9d179db332714238174316f66b13b014ca7adeb147264eeb83244cfcb7760b4b535d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c2afa66b4bd116e3a323f30d725f62698fdcb4afd2e756c356e197200000000f6ace32727afef136d7e5a2a48e20b3037314f2031d5714a00f548120000000093bd4c612c8195627e749f77483a7522807b4c620000000000000000000000001929863e2a8f4e0a636fe0268e12c554ce50626400000000000000000000000076590732efdcd078a94bb119f756dd10560a5827000000000000000000000000bd497a742c14ec56e2c1a23c7467cd097eca202d000000000000000000000000ec86e7440abd7f2a6a612c2dfb164e7c2cc3286500000000000000000000000077f8c906ccba480a742ed149d88b1947223a5356000000000000000000000000dbec8b348a46d83bf6779b7acd30f222cc78645e00000000000000000000000024f9914da133e440ad9621349cc0bb426843766800000000000000000000000024cd07147effb9155de15714496a1375d45b6d0124c9c033c5a09d0eb66f804ff0d6d423895d23094689d4308061316b065d2e03c98e5c17742af83000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017084110fdfc301d950c1431b19470637f93d9480801531d154db97c00000000c8f7264291ed781aa552405553926c57de35d158fcb44d5b1426281b00000000a67a296445a05b5e40db3007b483af751aa77832000000000000000000000000325b4d638276f73f12d8c016243b6f6bec8064610000000000000000000000005aa3ed527fe2dc5176ed3f70d0d3f506b98c8e22000000000000000000000000a27ac5556c9c9b589e4edf421239ea607c0054470000000000000000000000008b071d4e822d3b407e3bec3cffe229457c5c1857000000000000000000000000a2d35023cd7c2574f8eda81c05eb247c20d03a5e0000000000000000000000005df31661227ec2285cae770ffe2ba6347597a442000000000000000000000000fcc54b7a1f1b2b5d9f88787a38a9924b72b694350000000000000000000000003bda880a8f634c452cefe5335462ee3a917fdf3a22becd618b67b14ce8e2ef6a4833fe052b5eab7031b9ab28d5003a45d5a7d51ccb5e7e3ab967b20a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000688af27d9441c20a55853099808d86a7a1c3d4c31cfcf058cd24b3f00000000ab23f909f51e1d57043f08337843107225e92e7709ec66107d03601300000000a090cb7302024c6a88d863109b793733a601884d0000000000000000000000001e4870762b413555cc7e4e4dd1ace2172a700851000000000000000000000000c3b6a73da7795e4ceb38622d6928ff49fcfddf2f00000000000000000000000099122a28d33f135435791c30b672bd2f58e4d77b00000000000000000000000088f1662aa7c7c22057befc109279ce266cdfec2b000000000000000000000000ae3fbf1ba42779159b9d0b3a4df06825aeee45560000000000000000000000002a3d102946b28b22df689a2d94f0d170941b30410000000000000000000000008af40d2dfbab13009b2cd310c1d60d2bea0701280000000000000000000000007a6b1b405f6d4379c8ff167c317278325c1bc749acc67e74cc81df29a35ec6598d9ce7075d04532a495f9574def67c4ec7910231b74a397c51921769000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed13d22efbc3e64eb56aac5fbeea9f172d6576775c0cb946c1250e3b000000000d6fce31a210a832ef0c8246eac7ce75aae8da554b40a658ee2bf778000000000c869c3a27cfe309a88ef71d93a1381db56e575300000000000000000000000001475c36659f8d47b220f828fe75264419f7842d00000000000000000000000007e930668743513797131b0d04116e5dd9c2be0500000000000000000000000057c76a43fc1bf913dbc58948ec8beb371b9a6052000000000000000000000000795c571b40d9672c7d55e87acd50e22bd19d4a07000000000000000000000000cb8b3b07b9cf4233e42fe47c8f60024f6723e14b000000000000000000000000d7f5e7055ae00c720ebee2146038721eab472d6b00000000000000000000000071afb4612b65120108208547f1f73c72adc1983c000000000000000000000000c870b265030d5c7833b52b565fdc1a03ea3d16483a523c5bad1f3a0f5f926302abc2710b426e6e036f0c2f38a030c609c6bf033ad4e9a45090eea6640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bb3725b7bbb297894ca5350155bdf038092c7747f24af3d987fdf6d000000009050da4dbcf22763716e5b598e5a142900b58c38e1545a572c649d5c000000002b11eb08e70ace73de950b6d0973db29cb0d1e32000000000000000000000000454c6827b60c2b1ef03a614d5b2bd02068b01f53000000000000000000000000a92ead43c73e40321d4e670dadff4009bbc9377900000000000000000000000021df6c632a7ced4e3c5dbd7c1ad7a968be1cda16000000000000000000000000177a54477ab6ef3f3019ea07e440756b9c340f3000000000000000000000000035571d6f09a692741e242173cf6d5665ac6ccc40000000000000000000000000d3e7a661dc7a0d49e4f2f675b930b6549e50644f000000000000000000000000e3aa9e73951fbf39f6ba2d5c9c025d167643c247000000000000000000000000d529ee363c61992e77180c0aec0e8e0deb3c552344d5d064c29bd91568614e75a15c8e24a0aafe526bc3aa3e7bf590676449d522c1dc6e4f6af0385200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015473c74e6cd1326e966de2e558aa76ed45d615a7ad1076eee31056500000000ce93797efd6ea10ff7d2bb0211485f1d705711779443b727f068987100000000f6e9f2356fc60912e58d570e2f215d663b920c0c0000000000000000000000008241013902ecad03b0a6116ae0b86b274dc4ad2100000000000000000000000079e6bc38f8d2d56606000729495a88368757d72f0000000000000000000000001671d9675ac93544bb8a1d5cb2a924753627e4350000000000000000000000003de4706de0c6135a246dce5bb4d9983bbe87857900000000000000000000000044bfc71f5a78b8748b41dc776ed67055bbb56f0c000000000000000000000000320a1206346fdf320d9a5762c517147a4e05d73b0000000000000000000000005dbdbe3bb5ea373cb729de6ae28d021e0184fc4b000000000000000000000000724bc175f498062cbc464378211b466197ec483b39ac834c7e9c825caef7e874d8cb5402f4037e722570de6fea091a46e91ee53fbee6ad2f6583eb5e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003664946536fd3927d60b5a56cac87b1810a2cf7a43c0b5018536217b00000000bce23a2f137aa10ae49d2b16bda92c0393323e0ad108cf621d34796b0000000073e185652dba2b1d2763d27df9262a0117e7d3340000000000000000000000006052041072e3097793e8a43388e97e3b488ecb7700000000000000000000000079bcfb3fe3fba1210c3422316c596165c2a2597b0000000000000000000000000d12cb77b54fbb11d32a26158846af63d97d0d3c000000000000000000000000f3901b0233d16f489ce67a0649466200e6f15f550000000000000000000000002c30a86b58ce9d65b969d14775a33649b999964f000000000000000000000000d5855f7433dc3430e30c167e0eb5fc6445ae1749000000000000000000000000264e5553dd4fbb20e00f1b453aa1c04c6be3ba06000000000000000000000000ebb4a15681f0b90bd8d9787e11400025dc8fb234d20da059e051ab5f0b459832277da35c56b5b320cdc5ef4e14a31e0deb1071575b73241be9674b02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cad9d85e427fd90e1140c219c37a38321991a46d4c857c0347eb494c00000000eba79c76ad27e44cee2a7c172358ea25a56d5e286166333967326d13000000009a8d276e68326247b234a36f479898304ee66e45000000000000000000000000f9d0e902dc1ff7578e3a5367d06d9437c691b81d000000000000000000000000cd8483147b888144fcb98d2ccc7a444a046e2f100000000000000000000000006d48c147bde82011e42c1c4482a6665bfd91c60100000000000000000000000056097f756c20c10a5fe38e355292556455beb822000000000000000000000000ea92a3666d9f414fd27782639391c27dce7596060000000000000000000000002287da659201071bc92a047650b104172a24c9310000000000000000000000004d6f887a628a6638566c9f58adeb5248e2696e5a000000000000000000000000738a101a0a2955472547726bf8a1506696fb834b67e0645e21422173ccd9107e6699e62091ccd75a1d1d7c21e7748718019ff52b77927534b047cd020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008923f122a173cd2ef34f7c3cb368ed00d99acc0fd63f8166d8a20779000000000bb8585b4871cb38034a2a6bcf68a7675cb21040ad18f749f2cc586400000000aa79d01a53567d09841a4e02b6d66868a4c7746c00000000000000000000000080ed3605b1b9801f353db867a3b3aa62a9b6876c0000000000000000000000000e4ea4528e1f611068c63651d287ce15e0f1aa1a000000000000000000000000140aa712cfb95a221c18204afc0bf17c9514c5420000000000000000000000007576b1613bb3640a65daad64d5dcd82b88de2434000000000000000000000000bcb45107efaeff600379fa74c01b7b708a1e583500000000000000000000000038c0466a91c28a0f826afd51d7484863f96d2e27000000000000000000000000467d151387b8907a0fa89504ccabd40c823eb33c000000000000000000000000daaeff47c073403a436b786c7e35f820ae22115abca42a5d8e443f69ce78d46d5b7a2a18437b833ea50f237b6d957f0d23149c5e72d0274bc7a5eb660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009714ba15b879190601004c269f149c0a7d56f63e13bf000001a17a340000000004ae1e547202e720e1f5b02a66ab7d07fa16454c52c91217d133b02c000000001c825714af13655bfd536967272ae4338a23ef65000000000000000000000000c61efe5ffd36054c7e33ed1ad2f06d3a44c40c1f000000000000000000000000b64dd1386ad7ad26ae20f352044e9c4b8ac03309000000000000000000000000c257ea426a54ce74ad854c2f0fee2d663ffccd07000000000000000000000000873bb64e96b99d36e6f2230c0240f46d5da3620c0000000000000000000000008095c40df7baf25fddb9850cd85532452c710d360000000000000000000000000af89f269ae4f55eccdaa5326e09064ea7d3e706000000000000000000000000aaa17754c213686ab8094d6df86f40358d1301480000000000000000000000003474e870af2c4e7e90d13d0a75e0bd22feeb1123f83c88782cdb1e0e98269e38fe9cad1f27c1363daa49b3288a609c4a706ae817f41d032cfe8664720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bb73c5b02a336349733e8114da7421e03688421feb9824687c508070000000080f0a1770cb0fa277a06ff01553b830f886eb52f02ee12605f0b523900000000dde85329b9a91262a8dc732574899912ff127f2c00000000000000000000000030176f596e3ebe7a9466037bcffd5447e142d56600000000000000000000000071897c3cdd63271337a3d6404c3d7128d632092c000000000000000000000000f02aa44efddc5a0f101ca3136660df053bd0f618000000000000000000000000523f0d16b281090939f08308a8ad6e27c19a2526000000000000000000000000a7de2c16d41c00202c18a218d052ea5719efd2340000000000000000000000004b9c9d14c22e69175fadd57ed3c87f341edb55610000000000000000000000008b22be25cbfe7e6cfb408a1366270c340449316800000000000000000000000017f018152ed14f36811e3f45816a1663fb84461400d1dd17685e191b6278eb57d3b05f104b72c85ff8f3bc13619c174df9d4b9364450881ab04035730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002012b3711d3f7532419c5d2532c8ca449abdb35f9ef22f0e8e5d4b09000000009abb253e06fe570b8cd122755c83c21b91edd274c60f2e1eccc7a7580000000000fe154e8e53e06470fb6013bf45c96345e95025000000000000000000000000998b667edb68bb051c0a757274e2912d2366261d000000000000000000000000620819464969a0493cbd7a2f18d0810190ca0a590000000000000000000000006840c119bddfb918cdba0446d33d3b52acea486500000000000000000000000040e25400f3b39858e1b55e1bf0dbb97311bc0a01000000000000000000000000ba18857c7644682aa028834c26187a3a7f20253b0000000000000000000000007f8a0548350efb74b7b3f46e63b5821f4b03b875000000000000000000000000b0e1173228a2055cae260c1aa07f46403f12c82300000000000000000000000018c7466864e9594a23cf6e49d8b9011bc30b0261e2338176a4a3fa693c28681da4052d1da80ad17a52c7ca70bbebbd204be7544432afbe521919bd0e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cb13e2429366450dbef86175c38ab5da662fb5d422ab1441930ef5d0000000008ec8d2dea85e0475ef36f48425563224268f3124034d66cf5ae317000000000e64f0c58d6802a2746631235fe721b507ec4c84800000000000000000000000053fc0861932a81411d3e4a523203344f666b9a1d0000000000000000000000003a3f30526626f70d6285e1263715ec4370f8625e0000000000000000000000008188c207ac67cc615af4780f4a9ac168d729e93200000000000000000000000098103e709bad1a44ae628a09ef71d71bfbfe5d620000000000000000000000009f68fd31a76fa5361072c979341c154f33d67b07000000000000000000000000b112945f1a87a40faae8630bda310152466a376c000000000000000000000000ba134f518572bc74b60ec1313d2ea6423ac652430000000000000000000000005cc3e706e63a403ad0c03c419fce0a4e4356381bfcbf7c23fe60eb1e84c02443e911a06869c5df79317e4625601d3e26173ebe2b6a9f43314976c638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f39ea04fddc9dc7c06f4b65d7702144f516f7f7a55fd6b753450c43d00000000601aaa6d227bc65ff521894106b3b95abae99771ca76fc229961867500000000124838218a087c1906a1531b4374e3455e906240000000000000000000000000cb80313bb32aa361be26c40ccf4c802710b38516000000000000000000000000107b934ce1d0c947209fb9507490ee50629f0c3a000000000000000000000000b724fc7198b16d24179a731a51a01c5775e084590000000000000000000000004cedfe375057d039aaa7746abf92be4399b62b2a000000000000000000000000710bf613ef13864a14746604471b7300c599693b000000000000000000000000b5d12c10d21335680a83b56da947e26b7e8b91200000000000000000000000008a18ce518ea5945a80c15c0c79bc704e4f0f14180000000000000000000000001ce2a52f3b88dc1f04bebc101858b5609834675800dcc57e64d3db56482326773e755a0330d5372bcecaeb39eb4ea21e25c1802b0922f42e8137b6160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f86942cb923b035afb1a4544d6ff2520f9dbd7d3610f9187b80c936000000006fc62d19a34a5108da5ca04d9f956535c8fa0f77c129b20dfacd5427000000002334065e4dd552184211583c6abe504efd15491c000000000000000000000000ff13615aa9b4a05270de48202e862528e91b2027000000000000000000000000fbaeda4a62f367203ef3fa75956256336bfb6b2e00000000000000000000000057120f17be355616ff08a00d0b7e9e1800b88a3500000000000000000000000089719548f851d6368393e15fb71b79493f698c09000000000000000000000000afc5a1317def933c9201677312ac7a5bcbcc030f000000000000000000000000222229480331a85bfb2c946ec9288237ba46b92f00000000000000000000000059732a73f6474c2f8d4c06674b673371b887127000000000000000000000000083cc7e025e8a360d35b8b473efb31225bfd0bc6c0067da6632d81d29d7105055e374211d90dfdb5ef19ec64b04df5d0bf0e493107023714a45dc22190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005918492f6e710f0410ae8b31da58602df94d27362218e4427a36203c000000002dc3980b4048cd4e74a3373b3448dd3c1a4cee01f4ca4959d0fa294a000000000a45b30fd4cb333a90034a7d956fb62ab1f1d94f000000000000000000000000c7c6f3496d2ba30b8601c60e34df997922d00c5d0000000000000000000000002e66ef7e2b1969737ceda95d36abb878c127d01f0000000000000000000000003d24a5324f04f1756dc8ca0d7e2dca6d2034845c000000000000000000000000145dff61a21262654427700eb6c0e91747c8bd75000000000000000000000000110d7c62749b9450cea88c265d1d600579885f3800000000000000000000000061c91d44e8e898538d28950dad89d32a44851e09000000000000000000000000c1613f465aeaab07a6470e69f00e5573002c495c000000000000000000000000e3f9a63b892e4f29536b54393e733d568004e3464e8d7523c36b0e61f9cf445bee7bb77aa6f1162577899d252162f805ea2f2b008fbdac30b2126d43000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b83b127589b24c46eca6a11e63cd1939b1a8ee704e1720313560204a000000008416ac6ab7289b38e11b124b6498e60798b02776c31c011f0bd4a12e0000000092659d4ae54ee735b9d14a245e3b27177bb5b96600000000000000000000000068b42f50c45eca112573762c2f35900e776b100f000000000000000000000000d05b0c64f7ba0b7b27783744f2e69465559b9b3b000000000000000000000000725b93055ae85138c609d854b891764e17fb0c72000000000000000000000000fdca611b7509a518476e9438f9f9135b5520714700000000000000000000000015ecff76f5304544089fb974ecd88a389ff7bc240000000000000000000000003894744c4383092bf777257e3f6ba903f41a344c0000000000000000000000007dac0f42a3c78a77c792c97ecb73953fe99fae560000000000000000000000001f7018577b8854488c5a0d63991ffd1aa5f816124e39ac066cce20245b1059598f64c6583e35226658c044380b33f064841be615ef32d90c8606c8270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f030449058f882f3268176938e6940552d60a47baf5693ab670257000000000e264cc165f62205afe00f570fd22ae09b0120c550eabba175bc2616700000000e2f08718b76e194ffa2c773cf7fa531dc9fa872e000000000000000000000000f584e90762da714d93fb7b4e1ee7f44ff2fb5e4d00000000000000000000000030ca5122d758d2461276af7d08029c2cef8db032000000000000000000000000967b441ad687836d09cc570af2f404662fd5472d000000000000000000000000fdb7522c89d8874e529d723619e71c345921a43000000000000000000000000058a83302b37ba617993e9901e131d6192e7bf253000000000000000000000000fb9d33393f9a8f2b12b46a48670eb80f994432120000000000000000000000004b9011590dae9434c1f29020aa5a8369dc150768000000000000000000000000ffc13b0658cb824b54b5114c3de15370dbecd4146fa8c61c1e0e001c2008a91e4c90da2118d2de6cb2381972af8fba34506c89359243915c277f806a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c59a20d30ab112b8d47180dfea2721fc1fb1b3208149276d588e63f0000000070bac00f0e805d103364cf486078ea4c1ea9d94e6afad42d9c819e3b000000007cfdf8149e604b78c18f7b197017fc696dba91660000000000000000000000006372b139dd38861d2401bc52ac51705def2df4520000000000000000000000007adb1753cd1ff35b5bc1a2350ede5a51ea0986310000000000000000000000001c137a54ee699817d42e9509d3ae083f6fac6c12000000000000000000000000a1f7357940660a435b1a231b3d47646836c3642f0000000000000000000000000679367ba71b36119cdda4218951c11e5119145900000000000000000000000034c00f192719d8178c483e31b567581ea0ff161c000000000000000000000000ab423f6e61899801662bd04a4f90055b2705ba6400000000000000000000000066b0703674fe0d1400650c4d7ec7d343e4737c4398d7c936e499ac11c815e545c63cf071ab62ce2f72a26c61bb444b11bbd91b0b42d869302e3b6b3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097dcd906ff17f557e5fc47737597b86198d6295aa779217d5884c67800000000a7309d0405c55b5ad403d936962b676b8544e13ef70e54020a09f54200000000f798ce39e21e79057a149a7938396d496bf44c070000000000000000000000008b9e285e98f50a038c43103c3574125bed0bf15b00000000000000000000000025c55558515fb905511ddf5f11a58667b9a9ed16000000000000000000000000bccd831acf15785fde53e345883fe928d07ed13900000000000000000000000070c85f00afadef13aee93d6aa16642775b03186f000000000000000000000000aee7351a3493912be2e2644b38543f5430f4902d00000000000000000000000041715b3491036f426b23d25da71c2c763fe4354f00000000000000000000000017e64f75f151df5c6912ae2d37b6877532be69300000000000000000000000004e50db56a79677092ba7b7016c6f802ab69a25633bda9709fe9f522b29c113537e90972673c18e17e557c266bf97da42942e321f42143b7c64e7592f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000515ea8795797c1319155c54bf57ac72346dedb31b578892ad811f67500000000ec4b3372d7f0ef01823c1776556d433884835b3a92c2c9044a48111b000000008fb09b2a1f67a81d352bdc555986fb0c9eb2e773000000000000000000000000416e510869ed8419e85632150ef87c4acce71326000000000000000000000000b8bb1f380dfc705bb90b8b1c1460782985b3e5330000000000000000000000006c188f263619b96157172332b0632877675b0855000000000000000000000000038ea10206450065c9a7081f71840b1c7f955061000000000000000000000000c2b0ff6e67902d0ca2f39f2e3aeb557157aed12a000000000000000000000000c770d475c4b7510d052eea6611197f149ff47b140000000000000000000000004b1fad5e31d3a52ed3a0981eb4c348260cea925c000000000000000000000000e679381763a43a6997be8202c9b9d7770345d05ca4245b299e63bb6cdf153416a12fea7a7383ff3c4012e8247d6b151ed97ea95dfe751a3b4430593e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcfe2f65a5350c1b61a6c5011cc0d21886548f7ded00d518a702a257000000008d7d1c2aaff03d2a24424971befd4f6635667c3955510521abea2b6600000000cc2fb97eb9eeba76f703ad5d4f7350079085f22d000000000000000000000000675fdc6602727172c18389394b3fe82a1f06cb1c000000000000000000000000d183a2103625d72b3500f7712e9b483e1a7d061f0000000000000000000000009004c413d710c92408a59e36e95fba4332747c12000000000000000000000000ef71696f59757c6c3a296e439674fa5860355e3600000000000000000000000042d5e44d0a3e983e6e8f5e76f6c06f4e83859f11000000000000000000000000dc2ebd60ca2c7a73cbf6921e09196870aeff542500000000000000000000000081e04b4b5fdec86f05baaf2b4506b115d56d99000000000000000000000000000a731a144681fa0d0de4755972ebf64e2e41521d45e53b2781307201960106129d012b5994d19777e21c4b36bc40937a732e59082f36d8299971bb5d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008dedec3925b99d33e6d7ee191e74922d0263201daf9c1a6e09269e560000000091b6a41460b1ad2b94f81214a3ae4f7c8bdd830f94bdfe08d916ea0900000000848d366b4368af503890782d5642386b20f07744000000000000000000000000e531f640b06945428c537307214219597500df62000000000000000000000000d0424d601beef42807c2d91e945d490f1d0b5869000000000000000000000000ab0b3f72c6d01e2069094a17d33c073d5c3a8f3c000000000000000000000000ff13401e18be113187cfbd119e4595187637ec5100000000000000000000000006a63f49c432ab035064570c937b054c0e856837000000000000000000000000d104e70d697fba5820adc270da3283149590f478000000000000000000000000748e3e7b74a965507caf2f5b5b939d60a0da751a0000000000000000000000009333e55a89d042674a4b426a3b4e7c7a7f5403597cd2d6330eab2a278a48b50c21a14e599bc92f61c3aa0251ce72ef176d41307469f5b50107ae247e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e28dd4dabe4141df559d17d7ad1581b0061151e09ef7a6f15c8d747000000007e00475b9992a574247e82476372974606ede8139682b2597676082100000000ca4128509ce8f40278d009532ef8071b2e96b1420000000000000000000000000b40af2639cb8d3e18cff2685a40b96afbdc7401000000000000000000000000859ca516cdd17d6443c40a3eac8c9b2ea419c90c000000000000000000000000bd15983748bfb75eb97c3a5af5038e70eed46155000000000000000000000000ce44a419c9c6716dd956834001293d333c70350a000000000000000000000000459ee55a0bb3fa0f1a9ac32ae31c0377c0c164110000000000000000000000000f3bed527e6fe06ac849836ca038c91fc17228320000000000000000000000000294e92ba8c162774712d1624ddee96963acdf630000000000000000000000001d66472ac9e1d911a0ca151011d4fc26869ea01e1ae5820d5c0ea946c4233b4e96c1091dd83b7141dce0bf1179a99a5a1bacbd71d83f9544e99d254f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099313a123cec4103682ac117133517547f0a6e20cbe4b22834eba23500000000927fb96862aa0e08012aa2117012892d31d93861ba39a053bd9a103a00000000478876291854352bb925222853bd19461322a77c000000000000000000000000938edf402266c452f4056a584535835355001874000000000000000000000000d3312c74a6925a59e19e7a2391069b3848235462000000000000000000000000818a1b776559ce172486637d3f5c217386f7be0d000000000000000000000000dc290a6107cefc5fe7df6352681bd11878b3ff62000000000000000000000000ffd8434bdb8b6a3bfefc416aaf61613ae6d39e30000000000000000000000000c9888a242547d11d5d41aa3e33e9dd6854745572000000000000000000000000f469323bf082b820c2c528194e4b1a6aef99b846000000000000000000000000c9595d1348e9174a9efb06162bbbbf35b8b6064310f0ba457511200e3fad6d2576497f1f81333504d1409443304f59690f2457271e10070ecc612f44000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4df41573c6f9b3ec40682147a2b8c59fafd247d6e38bf5ee2fcbf790000000043b2047a69a537600d097a736d86e7174b7d3d583c4da94bdaaa770c00000000574a21162c29c32789878b37061a7c0c0cb2290400000000000000000000000068356d02e88a6d6c9a4f5c7b28c83e4d5b4e3a6f000000000000000000000000b5bced0ae249e41ec8d02a12d3d224154a06c90b0000000000000000000000000c70896bfe71fd0b04cfb57c9e76900085b6f954000000000000000000000000b05496382367936a4378a8042ece09794f3cec090000000000000000000000009048d60ed14c3810e32383262c1e2055d1b038460000000000000000000000008eff1611bf112a4bab5ce63691816a6fd52f5916000000000000000000000000e8d8675017e5dc61cb65ea66058c313de313cf14000000000000000000000000366337009f4b18010fe18c4471f6515775712a506c55d7631bd3cd036ec7c1567e58702b1923f30313b74d363b8b4e51532cd430aaed61746d7a21130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d53d946d6d64a6d160de93f02e7667a9b8b3254e914f32fd4b07e0000000000d6030a4a8e47560bf6a17b6e8d67315dc94c49151a4ece60ede86d30000000000450f61c5c07255e59ac6550e5466c0dfb9fc65700000000000000000000000007b9c41cf37ba73f0b3b9104f1a8f276c14c695e000000000000000000000000a5d9b8026ce6396ea7d4aa4b81130464b2c564440000000000000000000000001cdcaf2fddd4f24849d703301e31df790f44f85c00000000000000000000000061ca4c24e6a64058a666110fe637a90ef76b8b210000000000000000000000005252c95b4e34b44cada7565e45a7b052fdedfb4c0000000000000000000000003a50c8376f98711b112517062c638431979f9f4900000000000000000000000092de2547145c6b3a07c7f25c23834079431f6c5e000000000000000000000000dddd281f11ea7c3a78055b2d3901da1f8b02ee546ddcb90acf896a4dffdb3f29d7f5080b9b60062167f46e0790f6d94d575e3a79f0e6175339c19e4f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ab6a45cafe16019459ff37b4a23371725a5bc71eb679b49f6fba7000000000001fcd640c5935742300455576cce2d14c3e9c67d5eb39d39ae02cc51000000007b6f9a460c5e4139d42f1715f75b2755f21d8e5b000000000000000000000000ab7c234ecbb82c6af43bc20d5d7c036fa7f7cb340000000000000000000000002617180db836ee5aefaf6d2de511816d5fa44909000000000000000000000000e1aed935af81271ccf465415be37e9652aa6802d000000000000000000000000d20ec4735f13dc2a384cb074a1c9d010edc9160300000000000000000000000057f6ca6c68f3031aa7665b460cf6a43978781027000000000000000000000000bb8db90ffc998350e3732936ab0ddf4ac82cca7000000000000000000000000080e0d65943e5b71c075470293c535f1287b34043000000000000000000000000a36b936d5f8c6e299aa5fb1decfbb434a2a9d86018aade3d088e9f44d1f8fb28991d34586a399f36699ca53169f8d12611c21f6882a7e90822f28e6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ab6a45cafe16019459ff37b4a23371725a5bc71eb679b49f6fba7000000000001fcd640c5935742300455576cce2d14c3e9c67d5eb39d39ae02cc51000000007b6f9a460c5e4139d42f1715f75b2755f21d8e5b000000000000000000000000ab7c234ecbb82c6af43bc20d5d7c036fa7f7cb340000000000000000000000002617180db836ee5aefaf6d2de511816d5fa44909000000000000000000000000e1aed935af81271ccf465415be37e9652aa6802d000000000000000000000000d20ec4735f13dc2a384cb074a1c9d010edc9160300000000000000000000000057f6ca6c68f3031aa7665b460cf6a43978781027000000000000000000000000bb8db90ffc998350e3732936ab0ddf4ac82cca7000000000000000000000000080e0d65943e5b71c075470293c535f1287b34043000000000000000000000000a36b936d5f8c6e299aa5fb1decfbb434a2a9d86018aade3d088e9f44d1f8fb28991d34586a399f36699ca53169f8d12611c21f6882a7e90822f28e6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006179a211edbdae39b218c923f28f68206981284abf17880bca37154000000000bacfd75fb2011417fda919649358de68abb2b6439c61893ed3f9a635000000007a96db73d32f6053747d77430249d05213759920000000000000000000000000e128e9034363b60ec9542d3288c2993ee6b92742000000000000000000000000dabe6f05af8e523af0c683485bdf706f2094eb16000000000000000000000000d2b61c1b79b3230723a6d253d8cdd11728e99c410000000000000000000000005809507658854a3c8afd79564266b56ef9499965000000000000000000000000e886f578c9101d308304a140ccf21a05bd0370300000000000000000000000003e81611f44a831195858d30ac656232331d6ac4e000000000000000000000000d2ab6c12a51fbb4295f6ac3c1ea7cd4501b4b710000000000000000000000000aaca7869fc7d274f54beb415a00a7213786a410e316a676af48cac272e198a1fb19a317e25ca4a70b1c0cd6051b3d00c3719d90854d1490d4ccc3c30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af37775a89b8ab366e42ae0564103207a392c6004fe1b77bd55e681a000000005d99dc4565724f69014de27ad85ab407968f3d150753382fec2d362700000000421fc56b8ecec6000d770436e06afb5dd22ab71800000000000000000000000098845d2434c31e4730ffe15fd8b3043eef5cd4620000000000000000000000002807223a3addea305adf4b41e97cf26179690d0b00000000000000000000000024e6a97a1d4beb48e11e8575fcfabe6f0282540500000000000000000000000093826845cf917f0f33ecb3030fc28a700a8e1771000000000000000000000000053532590e70404c8e5e5545d0fa4965ca00110200000000000000000000000039282264f446b17d8909ee29e0f6a43fe7bc1e67000000000000000000000000e864c43dac25ea5078d1286b94f5fd2f3028ec40000000000000000000000000df6f9f66e232e145090ba4126f0fb4474709675feefd0e43f88f5a6ea6e6c26f52bea936e2399e04d21b7e076389cc627d1b9c12f88681748fb7d8430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e724a681c519850ff34083144ef495a3455a8217bd3e4519dac7a6400000000cc9a2335b89f61159ecd5c6218d71010eb19985e2d79be423a4367590000000001f98230949d8b3706e5114db9eaa443a13bc15300000000000000000000000046617a3bdfe78330fcb89c25153ebb2c55cc6a32000000000000000000000000c0f1f05c88cf781d754ad56dccb93f47776668540000000000000000000000009134797a51dd7e402d93ec6c6f295522838ca631000000000000000000000000895ae603474c95058283b82ffab118081537383300000000000000000000000028000b75aafc827ad929d6147447db5d7e325e4a0000000000000000000000002817e0270c74c14d97a2531cd4de4c2286b4564500000000000000000000000019de640d15647a78947d8f5efc31eb428021bd7a0000000000000000000000001dccd346f8e7db6634547f225ec3864257bb1d07e049cf490514811e0a97c34c2d6b0c334b23e26ff6196f28a684f52343d85f6de6dad821cae67725000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000266945041480fe4242cb7171154b97019340ec40c5f92c76df74d02500000000c2845b209a0b0e58f57fca1248bf5063c646b06449a5d823dc69154c00000000bc47cf3fb42d115c6ffa7d40e786436952e56c4d00000000000000000000000050ddec1edd5bf734bb4c875f0b939c0dd1d87401000000000000000000000000f5388a54c2addd0dbaa0a07b7809e40789e58b1e00000000000000000000000005f075406b86e649f0443b26fd32e20b926e3926000000000000000000000000b2685113255f5c65730c6b3d25a545014defe5510000000000000000000000005267cd27e0c6af6c22c5ad164b81777c84f46b2a0000000000000000000000002017b67ad66e532ae1d3451c4b29956c63c5895a00000000000000000000000020c6e90607b3be33795a914887fa831b516f3f700000000000000000000000002831f377ded8b852803513090caaa30713bf5b659b5bfa00cf75c96dcd653838764d892ccb7546187d17a932043d91483f4778247db27169c78e456200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070a20805de07e214ca3e16388b1ad6175050645a4365b90511a45d40000000007462c92db0236818d906d872cebbc765b4870601164a18113e726f3a00000000b0743e212ce6102e6520a016b3d442236ced442400000000000000000000000019f5ae1024ea0d7ea04cb07082e64369becd5e3f0000000000000000000000000ee22b304697531d2bd90e51187b60008cd55218000000000000000000000000b55ae113da505566c7589265b528b3274f533c6f0000000000000000000000008bd67c49b117bd0e98e77e55dcc47a5f9d54c6550000000000000000000000009bb61771e3489866d46c6073f110dd0418f9a301000000000000000000000000eb8c1c2f7edd833c5df4c4551fce4b5ddd75e1130000000000000000000000006815c90ef90c1d4398391379cf706b774c372f7c00000000000000000000000076a1a42ce8095067a4a74778225ac743ac9fdb3289ea5978d18b654356dc341b0a377d6bd63ec74076953e5b2dfd0d5d8fc0323281692843a0b5881800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083282b231366fb6d5d25a100559a7c7719000966e9d5f52178f4f1690000000059afa334541cf022af19e16a94c9620f0135171bfdbe1310c6d4b636000000002ba7eb052f174b7968330732c2628651f1e72d2f0000000000000000000000004e2162148e5ef50f6b92003ef0fb941f4906ad7a000000000000000000000000ed10cf247c0b40360855077023967168b17a424e000000000000000000000000b7db2067a151e173bd36b80954e2111ce0acff68000000000000000000000000c1701449f6d830421ac06a21b28eac2eb1de742a00000000000000000000000097f40d27a5e26233ffc639018c6a2f58c72c452400000000000000000000000029b6b93118e639783b611f2762b32e52a69a273200000000000000000000000014838f3bc59f81074b4a847085bc8732c4f4313500000000000000000000000077054d6a19c00d4c95116933a6045578a1277c5a08848a1820a9753f56c1865e62320110007cf15ec387fe0ef17fd00ae4457820d82b7215fa28e14e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d014f653fa3cad71fd82232b1a4e3616ee37994ccd4f247d554a084800000000e0ea574480ac1428c93c59048364a52d08987b64b60d2b1871fda45900000000ee834a2d219002188ef1f03aff85683c171e0b170000000000000000000000000fdd9c09753b594119de926d240ae116be82f4530000000000000000000000007ace68554d492d1b78e09b766dd1f07595533a70000000000000000000000000b673712da203f713d761002ea54afb111e3a034100000000000000000000000028af32509f93d16fde91bb315500cf25c6b0ec35000000000000000000000000eb1aff742356c04876569b21c39a534ac7c7746e000000000000000000000000920f616888a1d9565703fd5346267c501984801c0000000000000000000000007f27fd468648f17acd56653739ff4e5aa5b2881200000000000000000000000097bf5e7a87a7be18fe051c43d98da708073681105cf6b85f004e2c0a7ad0981dcdefcb64508f6e4191db6768b2675e00a923b26ee798c50aba6cb124000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7ff44269d8e02550266901e1b0da21d520f36122031233e9548fe0000000000c3288a73d6da451c0048e66ec632df1f650dd35d9304552c5974147200000000c4a6305ad80ef15967ae5219db0d7a0a4520aa15000000000000000000000000e950fb4a4474ed3185112d203acf16349248284700000000000000000000000095e7dc40837c9d2e2318b14bd5decf58fb78b823000000000000000000000000f7a4954c97b39959807c903a32821212cff0c20e0000000000000000000000003262846ae43541012de06714bf208413413db377000000000000000000000000055c4b541e1b9265d5d99565f0a1fe0909496a530000000000000000000000006760c019500fd917044e56297b4b4843ca09024300000000000000000000000051c40c72ac217707e0eb9b3bf5ae813bacff990b0000000000000000000000006a4d6e00b9bae31abb27b80c532b1074fdf6f333a13a1b539acc2569b2d128485df0ef16f410dd6e5d208f26b9fd2e3fd353e03d1e42183b1d37a92f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b574a1146b111950a484c321f6d16f55558c1172c8339f0ceee6d300000000004a8ec02ef4d25043b398b21742ebab13a38ca414bfbdee510e7d6d06000000004757ae28f906fb61602a944a6e9f697bfc17ec0900000000000000000000000035be9c1e8d9d62283e4192170a919a566ac1bf7600000000000000000000000019d8ba26e1228b4ef839dd079d6bb8105968b844000000000000000000000000e816347c09050d22997ed33c44690d029122ef32000000000000000000000000717fd252b8b1611e6592f127d37ae97286f20251000000000000000000000000bbb1ff029c8eba22b340d43f9b8e840dbacc0772000000000000000000000000574ebb36ae742459c234111c7830735f98ad5a39000000000000000000000000159c4c6208056064a02cb245bb0db16d390a5c2e00000000000000000000000031a05001e35c930ecc7bd041b508ec142168f6504b44bb3853eed7305c870c04f430b3153521750643df2523f9263a1fc071111dea554f44c1f8b623000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4c8d5683c9ccc23aa4e7d040729f03e12900d620f2ce554912ba34100000000d1f9fb0cb343e322161a092aa7923f3774a9a9353d358b2b3a32ee30000000008b01f463bb29f918ca75ad7800ea13628c09432e000000000000000000000000d4f4fc643937455871f1d26412e2ac203766ce010000000000000000000000006187220216ff81456123056964f0837b524cf915000000000000000000000000c7b2be7575f0ba31fa3ea665cca5d3219ac6b6500000000000000000000000005ebfe71a9dcef634ba173a7d44f95e483b10556a000000000000000000000000882a6e25c3cd040b1397bd3980316f2c97dc901b000000000000000000000000de7494386df85e4e4753bc17d1e4005d4b743642000000000000000000000000587e1f4648880f4c5c92125e190ea95077c74b46000000000000000000000000bdc79c09aded1479cf620e06c73ab249fcfcb018955231350d3ac67dc62be96327098164453180590cfa66364ec4a1627a544665aafc5045703c9107000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c715224e26d8cd07be9050174adb76742747f16f080bae37250f047e000000005183b4350f030528c40854556b68416d0e57af29ad1ee34aec1f7e3f0000000025c0257c714bbe7d0fe6e830c8854e2dfe87ce5d000000000000000000000000bed40c4fd848b34673c7976d7490e030e0d2355a00000000000000000000000074a69e6afd32a42862b448055263ea1ea532df7e0000000000000000000000007168250c5f57221a8eb8597bb1329e5fbc57be15000000000000000000000000850f8026a547e37dee9a00053110c367444ec45800000000000000000000000070b14e2330203d25992b5b00e950f273d475424b0000000000000000000000000dc680517444305f8a105b0344cd5a41599b0c5d0000000000000000000000002952b83822966303c922402d1982223df5b0761e000000000000000000000000c8cebf4ddfbc6c644b2c0d79d771364769717d3f2032e5502e5d1f0b8c480e4813b64d1e4766487db0c98a33fdc0dd027ebe94033d6b8c77d946ee5200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051c0ee7101ceaa6c38461932525bfc3c7550af52a1c67b3084c7fe6100000000cc9a0b1d94826d3ca595384a7d514f1924e631434c0823083a74751100000000a7ee66734be6c3244cc33419653bbe3a1eccd0310000000000000000000000002043c43196c915789d095c2bfd7a611b4a99bd2a00000000000000000000000003efe640d7712475579986783e35310ca47bfb3900000000000000000000000055aaf3202dc7e43045e5f814d0f0696230b9dd000000000000000000000000005b9714645cbc2a3051cee72112b4ac5fc1334f4e000000000000000000000000e1de6d346d938255de0759612294850e6d589551000000000000000000000000a301e748ada8164266b2620793f9402708674929000000000000000000000000ebe9bb3ce3457f7687fd84333d6892754d145f42000000000000000000000000df835b1964c2ab7bf5ef361089e2ba1681971537176267251250c82aba811a22e5742e1e1ade367d3a437573b3e7c35f7636bc04ec34905e10470600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de79df4031cda426cdf82f3ccf6b6f45394a3172f4d75559c0eb40220000000014ac92695de75b18afa54670fb3cdf3343d9fc0478fb3d1f58c5454700000000099fd43a0013ab5531706861b2d1cf2185aed81a000000000000000000000000f09cb32272ac1d356bd29a5766def6297e507469000000000000000000000000797a3a380281092c751d11765b2a47419cca322c00000000000000000000000053d17632102c01115b63d528aeda1d653ea90e500000000000000000000000004b85ca7485dcf4371ca0ae476d2e837d5729a4600000000000000000000000000dbba74a77116e4c2bab2537f8c7ea29f2d04a000000000000000000000000009781117d0ab800186d3b95670ae55d77d3145e1b0000000000000000000000002e5e0c21337ec678f30d92222d356049f4854267000000000000000000000000b5fcb84453a529324887450269f89d48d9d8396ab02af21b19401c10c8aad5316c135d7330b8a35d759a80793e4faf5eb4db3a436715dc7ac62c4838000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c82b9b34bd1c305f6961e475a434e2259a59b563fff6fa4e838d7c6d00000000bf431c103fac1915773fa4671d18a734ebb2406b5d315759e525e25600000000d9ce551f6760a11001074e4f360c9807d12b25080000000000000000000000006004922c3d33a922e6ed5b6980d0d857894c2921000000000000000000000000af6ec0474fa67362e58857225ca5a5496a74ad40000000000000000000000000d626b05724865b6e56e5d258c5b6b757cc93942500000000000000000000000060b0227e9ab1251ba020a34d4dd44f1a51cb940800000000000000000000000091c06a66c456305bfce1cf495ac4ae2c884f57330000000000000000000000007c672f44a531154f5d6b1d75bcb9ae39fce534010000000000000000000000001aab32032f95f9672da4bc5b9b51003a3e5e6e3700000000000000000000000011cb847df6ddcf073d98cc4f8be72c66dedafa101fab527ba30e4c0858c2be5b47d9967817279504ea3365116eacec6f4e2ae65930f4a4685cc72d320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009dbab937289f0d5463dcc80516c53d720278e760ea03702a07efdd42000000006898ed7d99ba146a56a9165f763a5e768de9b836b2cdac673513ea0400000000e050252492493f1ba984cb74b22deb11c37f847300000000000000000000000051d2bf2aaf11ea74922191205717084e0c4d6a740000000000000000000000004fa2291a0acf8e3e295ee52b15a0d44da237b42f0000000000000000000000008c331572b3a4816dc4d7e87d1ecee135df75285b000000000000000000000000f183475e1fbde10707dfbe31409bfd0db9e15436000000000000000000000000c1a1d22713aa7634d8f66e02b4aa5b1df87abd3d0000000000000000000000001e5048539c40eb5dfad00c0e6a394a4b68d2f4630000000000000000000000003383302f5289db7cbdc9bb7ade0b9a4ac3336b5a000000000000000000000000ede13575dd257819742fdf40c178fc409b5f3527c1aa774450d8a7045998865a9bee4a3fffb758324cae8c26897c5b3816e91a4b82f49d41e12cd90600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034a3c8500b081e68970fdc09769ede49f1ad44053532fc0b0467575a00000000d1fdda28759c0615da13a0398b3fde7b73f4301774880a4a7a59b24f000000002e33d0069bf8055097624709af552d551a07004b000000000000000000000000d1e40b7ace02a56cf29f580f6c1a6537eeaa693f000000000000000000000000e54ae23b4ff2e423722e4720d7155c0d2c2c7f65000000000000000000000000c4afd327c75d87675e47c97cf94c665c8c8d4b510000000000000000000000008a21820a8aa80405f38455347dd3546aa6a884540000000000000000000000006a47966e3bf34116b796697de093ce132be1a7750000000000000000000000008b6a505b029b375159769f1389c2d11f51383c2b000000000000000000000000f7438b3ab225455cc74ba146200dc678649f3a1a00000000000000000000000030dbea13345de421fe97825347adac37172ec876419ba570fc9d4a50382bd570102744158b459b34f5b4346e0ae39b30216a1013b84af26464862916000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028a2f32d3b10e3ec317572676e21057e2a1192b16c3f57e4f83207300000000915f3f684f1bea1afafe7218e3bc8348843b5b5aaa600509d045974a0000000076b51375394f7e0f5dad03614ee2c0638c1160580000000000000000000000002f384e4c7b264664d6d95b6486c41e705960b23b000000000000000000000000db54896e7609fe6a4ee790607c9f914a00c38e770000000000000000000000003302eb5c21879b632004764d76a15a450ccfed2e00000000000000000000000050c7142d3906ee08b70d7c26f01c6b75ac26c029000000000000000000000000c3ccfc3ced86794328757d21aae98d09d7dd2b33000000000000000000000000fa1b1774938bd12478a5ad0c1f727d7304a9f92b000000000000000000000000c8055859f43dff3bdc85891f0f8d770c33dcdf46000000000000000000000000bd448b5133410b2600600444d3a3fd21bb8afc468af7cd4b86c088406daf6909036dcd7ef763424df48390035d078a308232c37e75e68e1a1dad4616000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000785c6e5807c3af22e6a21a73788177673a4ed13b2bdf925125a1a01100000000287b8c3a3fcae15e2c8ffd79fb5e694a50b3684d29b46f617391766700000000afd04611bb939901d6c2d12d360d7637ad9e50120000000000000000000000007a501a602a0bd451dca41647c109094220ad2704000000000000000000000000d7285072338d7464f13c3e542b26b21abd056e6000000000000000000000000067c0d61c95c724215ede9e7b7721fd6743b322080000000000000000000000009b873d2d593a453860b69b1a089990274b757646000000000000000000000000b4081c4440c486101c23cf4b155b550011784148000000000000000000000000f7d2457e7fcdd06b865885784d77b5246a265908000000000000000000000000a48199096db7f22e4c8842056d6a052dd15f26410000000000000000000000001cd94e6a2646320512732f4b399f51081d902712368efb27a205cd0e27ed8209d839f14998df4c195dcc6f5e102c141523a4c91c2b990b19cf483c050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e2bbe06fcb1ed758335c70fbae8104bb9e19664cfbff4608a97604e000000008d4c8f4476a5a37434e3a9054443f22d6d0ab4096c2742036c168d62000000008b61716f2d8d532ec3be4d5f64dd9d5db6293d3a00000000000000000000000019caa879bd7d7925a3f3e16dc6e22369d634402b0000000000000000000000000cae4f144971372c02ccec134459c5237cd3e60d0000000000000000000000007296fd1cec1cd0754908917a29449500689c04360000000000000000000000003ae660185977d314da624b4b3c5ca81b8bdf3c6400000000000000000000000073ad4e73a422393f404b903fee82e704666d947b00000000000000000000000015e44e05199cb13ed568315d0d8c26357075b943000000000000000000000000a94bf42580acac70977ba959d6cb7179bda8b808000000000000000000000000a8b32501371584252b42185f96549824498e9112d4c8496fe97eb51775f89606fccfe900f70c4c5ab8511732dc26b4201641540c815a2553efd5eb36000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000119cf73076f4062937263b1696844955296b7a2b2e28c53ddb68064200000000e8df4c164c97716ca8832f3b818965000c8c270fb29fdc24e19d967c00000000bfd2525b82563c349eb43b245e7aea705d87e668000000000000000000000000bd6e94368eda1c7e476e01098e2005138f194062000000000000000000000000fc35ef1b3118de323f64ed4acb5c172b71a0f134000000000000000000000000abda6a587986c000b12df55b17ac5326869d76550000000000000000000000000ace3973b0ea6930286b2c01a6a36d52e4122c5000000000000000000000000087414e3a40b41c37332bd7426e206e6ab5b824600000000000000000000000005ea07801b92dbc0af758053ad0ba060bc7c93b7b00000000000000000000000039e42b521699aa5c86b8060b52fc0e691573bd4600000000000000000000000083a4ea16428fe54d728d3d23b440e33a1074814242dab5745cb89564210e997a903ef6536fe4e77449791046d6c5786ef42d39238e8abf38be70494e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0d0c500404b055d7ac8690806a1b25847af1e0993b41650bc31ed410000000020e2793dac674c4eb136c06d1e19965172097b05e50e973d72d8300a00000000fc61ed372d0e9530e3086405bc09996771c36068000000000000000000000000aa0ab2071ca6231e770c9c01561de75430a4403a0000000000000000000000003397bc3b601a3e73aa89012282fbf53e571ffb6b0000000000000000000000004c5bd32d6cb15260499d37567b37df1170a6d9380000000000000000000000008781285b8fc19327cdb667577af1a67116100c4e0000000000000000000000001c66d035e905ae4e255c8759d72f9027fa0c9f440000000000000000000000000717084063660b15b1a8005f11baae228c50e100000000000000000000000000be89ac4900c6b5089cf0f6485790bc4ef7a7956d00000000000000000000000085673d6184b4ba6466e16b7ba41a726fecbbeb3b4ecf4c70a5bf9d73de3fbb4cde8f3a6a99a2140b567c4b0df3facc2dc353320983bc4a2a193a4500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077ab128c43c65102465f40c9d76997a68a57f333e019d3d9768d44b000000000cc1ad630087741d5231f80cb07a8318408a8f73c8c3d627b4dbc96f000000003863bc000acdb2157da3763961650961dd68e86700000000000000000000000030e6fa79abea987814a03a7c7f2ea73dd8b9375e00000000000000000000000002ad261c5659bf6b9429f75c9a28e1753fa64c740000000000000000000000004f7b6613e697c86d4062036c7c33f27cf2be881a00000000000000000000000064cd0a78a7070622726c4679ad0c5c723fb548050000000000000000000000006a959a7a30d5d00d1c6e065f9edbfc1e8b2213310000000000000000000000008d8b1f085f5f596576549d228147dd6f4c5594700000000000000000000000007c499e0d602350639aa0cb25a0137a28e60ffc1c000000000000000000000000705dc426819b2f269329fc43fc1075455790494a030cda4e074174048a9c38282e9b1a47ba1c240f52fc9c0ef588a27aa3e5d93acd982b4d0915b3730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bebd9575bcabe742d2ce90866be2e542088095284441d67d51de10200000000974e967639a45476d36b0a66e4dfd760268444322bf3b70e8c63444d00000000f568956a1a6a2d41f7db2276a1ee3659db43bb07000000000000000000000000147f6b49425e325f876c4b61e18c246bdec0dc47000000000000000000000000b2a54c4797cd171bc33a463d69a02922a3410213000000000000000000000000cded2f271ead5b0cc423ff7247325d0064357d5f00000000000000000000000013509f79d3becb03ee4c4240a3dea3376f15b80b00000000000000000000000059701074bef6f767b04967569334696697cb3a740000000000000000000000007e24ef51b77b524809e183022966076dbe0b1b3e00000000000000000000000037b0ef1cea53175292788b43186ccc19f4ba441600000000000000000000000043fa0c48a301c26f9c1fa1059a695325edb178084807ad068bbb0f68e5846727ef8b6147c1f6ce1f12826c672e71c8047307b00a1db4e5650c8c824a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c59a20d30ab112b8d47180dfea2721fc1fb1b3208149276d588e63f0000000070bac00f0e805d103364cf486078ea4c1ea9d94e6afad42d9c819e3b000000007cfdf8149e604b78c18f7b197017fc696dba91660000000000000000000000006372b139dd38861d2401bc52ac51705def2df4520000000000000000000000007adb1753cd1ff35b5bc1a2350ede5a51ea0986310000000000000000000000001c137a54ee699817d42e9509d3ae083f6fac6c12000000000000000000000000a1f7357940660a435b1a231b3d47646836c3642f0000000000000000000000000679367ba71b36119cdda4218951c11e5119145900000000000000000000000034c00f192719d8178c483e31b567581ea0ff161c000000000000000000000000ab423f6e61899801662bd04a4f90055b2705ba6400000000000000000000000066b0703674fe0d1400650c4d7ec7d343e4737c4398d7c936e499ac11c815e545c63cf071ab62ce2f72a26c61bb444b11bbd91b0b42d869302e3b6b35000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc5cb804c699472e35e2b9714c95f553a79a520e4ee38c6e1651c03b00000000387b12767d7871124bb8057ac243bc15ccb1d44c4a97fa3e03fa2f69000000006bce1230b54f593b6872511b45ae671e7795516f0000000000000000000000002f6078125e0a2d22ec96291cc490d4413f09cf2800000000000000000000000005939917c2c8535f21b3a466a71f4e30d1f1c17d000000000000000000000000cb467a22f174e15be563c1452e67f57254e9e82200000000000000000000000027e7fa6717949634af890e3114e06542595283500000000000000000000000002a61f42088c04523a96f265a0fb5157ed52fce37000000000000000000000000187cac0daadd514b16d8c1395fb9c123ad7c62300000000000000000000000008168f335f103cc36d7b3360b98ec6a7ec893a11d000000000000000000000000dba20d695ae02e34d68e6431f174716a274da22a73435e4f512bca262933b44cdc79120e4e86e471ecdaf22386e12774441b392eaa33653b12b54d240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004316a901320ae744fc9276228eaf1b0c476bd45d3a9a8c28f2bfa0610000000055b2d522c486857c9d3fcf2b8fadb13ba2897334612d760c0913e62f00000000eed03252717bba712872a31bc6e18a0cffd7383100000000000000000000000035c2dd58cf9db078cb0ebc40da0ed407dde8db19000000000000000000000000a174ea4b572067268c236237477fdb2ba5f25638000000000000000000000000abae85709953d7757bbbd409493b540e64de3047000000000000000000000000252e685fe1044340d8cc7e70d29c760468c1f34f000000000000000000000000b492dc0f2560927c409a013efe64063bc0f1ef6f0000000000000000000000008c5a5e3ca0e57b42e802aa1fec04f0515426113e000000000000000000000000775e8718d9b2d03346d6861f4a00750148add55a0000000000000000000000001b1f195554d86b468e843c3f9171f1458aa1a648d8c00d260451493a570567200836dd7596f19a4757dfe31f88da7e745dce58521df4ca308edb451e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cea0e3467286308f8169b742813a331ee409b5fe7335f035689a50400000000cea8d65f1a00617520ad84548488f818c19adb726d5fc054cf4423400000000042bbb7273bb85e4b3900c3199ff2014108e9c23c000000000000000000000000b5755f19e15826016da80d2bfd3093700b166657000000000000000000000000660168562629072d923a0701150c9122b232df10000000000000000000000000bfe0b56de840aa0d893b05098b53e0277c655d11000000000000000000000000b20d96773506bd6f7bce8073be9ad668112c0706000000000000000000000000ce17cf061362c20ae3ad6a66cb8eb33a0966427c000000000000000000000000c0a38412286cd060ef57e6535f4fd63537ceb449000000000000000000000000d54dc22feb5ac53cb9def10997839a3c38697d6900000000000000000000000041fcee5c451e1e7e8e6cae63b6d33852808349198280002878070365ce34787e64937c16f31303373650e16a4ac7616edd0a873b5512743310799d45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5618132b902692f8babde6078bc39425e64b37d7393114753252a3600000000146c405ec137aa5d3766a51cee63e44e4adde84bfd3cf763fec03223000000002e958f647c74427837715e31b6427b4aff044b420000000000000000000000009cdf2740453a5d7673f58e7707857007f700b21d000000000000000000000000da648f5cfa2b8b533da90230b8d50d733a3e4750000000000000000000000000fafaf376a3f1b6785d681f18632b5e37287a7065000000000000000000000000119a305a1eae664fde78965e6113284616e381100000000000000000000000004d43a5114b1975093c900e5be18c2a03766f1055000000000000000000000000fa259b29e19c4c628783da176482fd108192383b00000000000000000000000045403638ba554a15be8aad02c49466569ce108070000000000000000000000008d62116bc0f51e16dd0e8638392e835137d4da579f8454333e76f330767351224830d81f2900d93a76146a44e97536452fd53349a4426810df7ffe5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd093c63527cbb4a0d5adc031eb5ae65fc76fa2bc14e9c4dbf74950500000000612cf54f292e31075948930b0ffdf266965fbf2150370c79ee7215360000000052b15b5100b2c90162ed6006c458f35d02d4a554000000000000000000000000587c7d58d35c08673d3eca5ce39ae767923e36670000000000000000000000000376b23f405bfb37c8339419163c523d1b7e6a72000000000000000000000000c3cbc544a0fafe1619e83075d40b134345e7af60000000000000000000000000f5fe687ef862605da15fac018152c30877e5fd060000000000000000000000001bf3766aa7a0a02a904d1364d756b1790bbb8d590000000000000000000000004674920ea87fa65bf02cf51180d92a4ccccf8e2d000000000000000000000000d18c6f6d4a23805bccf39965030d4e53da72010d0000000000000000000000006817524bc52fca1ccb651440a482082209088b4cd3be82349d2dd639290a8c559dde693680d6283b914de72757b7e537bf7df305fc09d323051f602f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036004b7e5e439650b8f2d81a613f1f57fd349836125ac91f3389a5610000000045d6c16db896501d860ec714cdcb773b39c45b0dc68e9c565102522800000000d8613a6c64524d5cfd79ba5c0a30104ef04be42f000000000000000000000000fbf0e86dd50f114a69737f7e8f8225616426bf300000000000000000000000007bf72543a935ec1615e9565e6629942952c8f0550000000000000000000000003fb1130b2c86db181c04d737f48ac621d545b74a0000000000000000000000001b297f6ddf7de14ff3381c75eabfbf53301af05c00000000000000000000000015d1290ad7ec722452ebb60a40c30e065d5b921300000000000000000000000075c47102d9cb7240b775cf03d3938f2074bb1800000000000000000000000000dee9b97039df11639a76ca1fbce4db1b9b1ffb5a000000000000000000000000069d6978312e1d008c28c7731068b8222e1ebb5e633bed74bf057d3595d5ee5704974a3ae8020f2d4bf4921f6203f52550b5d06fbd944f354242c90d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022b555472965b8620bc995413741db558fd1ac61bc5a31603ea20c3f00000000b4acc42384fa0e533405192aa38c486148e163758442d410443f792e00000000fa21ca765eb54162fcff366dbf1a9074ae40b55e0000000000000000000000006ab26c79ed1d244a948fa931fefd9328e794cc65000000000000000000000000656fdc673d291c0d12d176375aa40a0a8fb26656000000000000000000000000536e4a75fd841a5c0031c21d8acfec07ccf36d530000000000000000000000000f12ae37bc99827280d6923e262231476e55a746000000000000000000000000c2f4fe1412ee5150293b5575c7655538f13d4a450000000000000000000000001dd6d617bd5e8878872b354eab75c01cf6f60c260000000000000000000000008cafc768e4248f328d24f869c046525ed234836b0000000000000000000000002a663d14f5471c4730fed65d2f388c0c96536f6f957a732e5e52c166c25e8f0f056faa337758466839095c5989445868b7e1d6776440f760b2d689130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000998b352f6706e5650ca8e30c3098a6421db050c0482933c7218363a000000004cd1cd406431392c6cfb4413b2a78833745ce6244c8f451af952e67800000000e66d401ad9fe1a2626d8007b30437f56930ec05800000000000000000000000008a9717eb0f9547098bae47dda27de236b055b4f00000000000000000000000097a40a313799da050c9b6f4b12d66253ab50c62d00000000000000000000000045b51c76e4a2d822bdf7d74e48f6a405e33777180000000000000000000000003151cc1be47beb38fd9f331f2b9dad1f8979756b000000000000000000000000e00dc54551517043c6f16826e8f00e3d83263b090000000000000000000000008a46e3193aa768685dbac828d717216871c2054e0000000000000000000000003604ec74810a066c3c9cd27a75dd5465d4c9a40e0000000000000000000000004610a147c293763d55ed664058a22d729e5c61335eef6e242357897ef23d456acdd2580ac73a7e6e557bac0512fd6e5404660d560184c1598e52e277000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c351de196530f017fa2e21362df7c54d4d852116738e4d2363dc13740000000084b4694efce29221235f0f23a985202dee45ed648e2bce53b6c41647000000004717bc4a3fa7572359d985589d800f508cb05f2a0000000000000000000000008e604c0238f5d53202493d1e8312ab73bcce210a000000000000000000000000195abb16f4bd1f63a8bb6558651e201ca931b240000000000000000000000000d7284a56fe82c27aaa0ba43403584544e2b0d26600000000000000000000000055709a2f8f20ca531647192139fb3d651b2c7c56000000000000000000000000622f2b63114b396eece9c93ee8fb1f69f5ea194c0000000000000000000000006b32fe755644d42208047156ec531f2a362442530000000000000000000000003d0f9306a63ecc7a5fecd1782bff29093543ee5c000000000000000000000000118405279a15775372bd8e3a18d7101bf893d768feac77042e77a8438d92d9103d4b444196d67d540959aa00a9895724b6ca7e1e76b46b2c9b82cc04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd59fa633dacab6d1d5fc470050cf654f05eb30c6ad4b777de95094400000000813e0458e13bd440b2726a03ff69636eadaf931d5984d1474084145b00000000b8b72240614f944d8591d513a4d3152364d1cf1600000000000000000000000034266c0e6f712c4dfe28667a2edbd0619fb2146f0000000000000000000000004cede00fcf885b5605cb0330a73f8d11af56861300000000000000000000000070fb4f0613f4843407bba04ac95e75490094111600000000000000000000000070d0000a61fa43411505431d6cd814046a9e1a74000000000000000000000000724c0a4450d41450cb54ff22a941467d624167620000000000000000000000000607721c99c61659d1289409b829f86c6ba3d214000000000000000000000000bbedca2a9985cc0544d9fb5446f97d5a8a9b4a2f000000000000000000000000791358559396ae7e7971a91da27b455d80b133514aab966fdfab614fad12763740d77a062da2b65c713469183f9fa75cf4ab3c180155784a1b2cd434000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e90e9009cc77a94fac2c93489fd1866cb65ecf771d521f49ce2d176100000000adaea10f2bd75d2accf3e36559f9bb200697746cec6c7e6708b1f44700000000fee5cd57f4b9bf34af40074c6c807f2d26b1627b0000000000000000000000008a6cd9334f7852518b4cf51d1e85e242fdc002470000000000000000000000002eeac6721699d30e818d2b10eef7776bc6223174000000000000000000000000bc0892498d1b177913530e49cef85d1b938eed6400000000000000000000000004577c38fad42d4de2eb87500cb980637f4d4823000000000000000000000000137cd96cade99024e084045d81a2982185b2001f000000000000000000000000f73b7f41aff391455b03c2328cc8202744aa785800000000000000000000000095ab643d233444007d50b76cf4e5130e43d43832000000000000000000000000017ca1095f4ca5719e0fd97de2497812dca61503bf93d75df7d5e553b63c167754eca11823b2bb5458e2d47bf1cd673db2bd290c3919491fda18ab1a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a1dfe82455919127fa4f08471bb2241061017e5e181ae61158da7b78000000008366fd2f98a3d818305b1228d039f475665b9f25a01f2f5b38701e2600000000346cd247a3b2f3187915b77955fddf03b86eea210000000000000000000000003d20a855f5158a35e1c7062fd89f214b4920ec3d00000000000000000000000013824b3d6f7d2e69698b9c6c06f1b77345b17070000000000000000000000000990f465773405f1f8873e974a22cef0b964c8e790000000000000000000000001c048c704841bf19c955a737632c9b4ba7edb01a000000000000000000000000be2a76691e58ec5339cd2e2b1a216c601ec43271000000000000000000000000697098102d9a871e34fe964392cfd829fb00035d0000000000000000000000008999f26dc8097d65f8d9666de238024bdeab8b0c00000000000000000000000077757776dae0e801d55fb61c59123c27cfc93c6713e11d691244c4677becb009e506750ec78a5e66547893336eb74c50f18c184a667136451042922e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052507d6c19909b4d7e1e256684cf7f54bde350053658fa1210067f66000000003293f4720b16d1648e0cd93a183aec04ad6aaa1c70eea13242d82865000000006823fa14aa83791948a5a0031fac581dd1044911000000000000000000000000b05b6930794c9f74056d64409955727e2fd50647000000000000000000000000c637252f70a35266e69a883d2c076060236556470000000000000000000000004b76ff76023b014c432a1d6b9f642d47812d686900000000000000000000000051b9dd50376a9d1f44d3c10144bc514e6541ea2a000000000000000000000000fc7bba19315f45553c970a04237ce86158d98536000000000000000000000000772a0f6c0f1a0a5e30703543d5994a3954c3b86b000000000000000000000000418cf748dc8d0c287405686b721b4b0821d2892200000000000000000000000007a9975cf311d5364cfb4a5cee72634e649c2140b6777b46e98e986191006b669a0d5b567fb40b74373db947926fac138208301eb84b950c1f303d3300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023095339e0b6740742e6df526dea4944c41d9671bdb0690b119f1303000000000bef0635e4d18007bdd4b52ed6215e282a0f6c45c3badf2f70882303000000004882a97aa9e68277a28f801800ade54632321a0200000000000000000000000053ff18677c693e0e3913175c537c12794e691a76000000000000000000000000c1a8104194abef0df303b72c89e09c66e70a191500000000000000000000000090fd363ab422c07decbab71e70dd6418b982a8190000000000000000000000006b311f08ecb14e71857d7f1adc6b2f68f62f5874000000000000000000000000cdbb656a16ade743041c2e6f3c936460bbcc8e39000000000000000000000000e2e0536cb10940291141fc40e9831b00a68c6411000000000000000000000000a531a4751c6d6e50d981e63ef1687b4f1f4280140000000000000000000000007c26a15f28f8c11820ce426daeba7248b59a06251b5be369aedf240049fb3c179bb36a2b13635b6cb825f374df7b6569919fb620247cb11f3431ba47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a785a85a2a097a25e342512c95f716171d8b5372432b8c1def08276800000000bbf29b2a90b2ed1af73e4b5f1df1470d00fe21559ca814721d2a601600000000e7e8d92aaea66175a3619c404443e105d995ce7b0000000000000000000000009b42f83fad13e93338bcf144e0b2cc5f8863ef440000000000000000000000003832175da5e0296ef88964537d500f6a99e4b938000000000000000000000000725b010d8434e54329992a70384cf6286152ea280000000000000000000000000682311c7f027b1855471658af6b454b4ad7040d00000000000000000000000008c97c463066694bdd07bc559de76f3d035cdd7b0000000000000000000000000e60e554637917457e9b303a46ca2a4cda711e5900000000000000000000000042f39a55d22c4801b688455998a24c4eb862c93b0000000000000000000000004974426c262837384ff9b8797fec9e3f9d695753e1db1a57a47d8e1619ff965c65d11232b442c5726377fc2fdca22e4e890245409d66ee1bea3a860a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011accf3993b85f2f9c88fc74b894536db367a16cae661f088ff79b21000000008f7a5a3a7cdd7717c681f86dbc01860b224f587da766285a7664db39000000006f40f5083c1c500d1cfb8e78b03cda5f0af70c09000000000000000000000000f55ec02145b75b48b81a534f22ff7e62c26610080000000000000000000000007511c41495f9e75eae526f64ad1c234691d7de780000000000000000000000006fa10f4c6fb5c85ce7a6e8695a0a7429db7a000a000000000000000000000000427b4075413e8f0b99d5fc0113dba76ad1c2d80d000000000000000000000000afbd64523493440bf496932283c3a605df8ee0410000000000000000000000009fdf865d48fe5a3ddb7fde01438c6e7b354431020000000000000000000000003ae84c04636cd057b02207397862914db7310330000000000000000000000000c3e803574f2f4362a6d6fb401399e15c4c36943a92f86d091c1712461b270277ee85d24ae0e7f676e517d37e7ed4d46f5836b21dd6ad0e5ae045f8420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c68ad64435a421b9d4aee73358a3277044537388bc8c624e0334d6a000000004fc2187a76644d1e4a3704470c0ecb1462da1a63683bdb51b0914c410000000093da1669187f7d4c9adac2255e937802ebb9020f000000000000000000000000265ddf4a8d509b15f3e1d90a674b8f2f8fd0cb56000000000000000000000000fc975716daa716438eaa7737d5cd431d3a8d82340000000000000000000000001390095c4141a03e9808a060b9f189767a325306000000000000000000000000155a482d33577614bc4c523bda4dfc2955486b7300000000000000000000000037841b472bab6f4c88e0cf6987661b271f42561a000000000000000000000000cea7e7436740da3a4218c60c0f491c0c24844d1e000000000000000000000000b154126071e79d23da758809a3fa173f0599355c000000000000000000000000dead4f6e72333c6af4f4d6520fcf852f2a7c0d2a87eea5656a5d873b64a286414992fb1eca38005877237b38a9514c22a435b12a2daf7218670cb473000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c7f0164733f70c41c536c028c35c7c46c9170115ad94535794a5567700000000c076f81a9427934cec58342025c44923d1de58567361047bde0f4a7e0000000024e1b642e8a737787bfb0315b471fd5d7a323012000000000000000000000000d33dd44c6505b306cbfd71164963d14f076f3e71000000000000000000000000702d3b4ee057133a2330ec5dd921c5053b429053000000000000000000000000993f3e592c0d00676878775b95e2480f228d5a1e000000000000000000000000650d83539f16732f532c317c47bb092b1ea6bb48000000000000000000000000ccb80903f55d951b09d5c4029f7c9e4b27a7f85a0000000000000000000000009ccf8f23be7fd07cf128d01ae5a8735b3f9da14000000000000000000000000046b23c755bfadc3cb22c8840983ba71325d76d4b000000000000000000000000b1512f70ee65192cd4fa220c50bc6017c9c20f3fb253c24eb6a9bd6c797ae857127bc66a08a3dd7e8c90d33ed9cc171595f1b641a6f01e63f384ab0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d57665bb45ea6152a9ab718fefdc01a7b56665c0b65004b66b3af4b000000002bbb006e75122779aa732e7e52a99d3b1ce4ce702008892dd8f09e4c00000000e16b156ffdc6083c0e9e6f4ac58b54787e5dc16c00000000000000000000000050084e2d8942997626416f70dc2664089dd5e370000000000000000000000000917a40788410881f327cf526a632d133430a4a51000000000000000000000000acf2be68c9447448662c34396bc9e70ca5894f74000000000000000000000000c729fd65cebddf73c183d1757088bd56b110f450000000000000000000000000b69aae295392294893af0a5c3daaf26933dd6b54000000000000000000000000a759463fc98a941d0103df1139654c2b24449e7c000000000000000000000000f6b92633f2c0bb4107890f614b24f003f9fb402a000000000000000000000000c19bea3feabe1b3e10e49a3cc7a2ea636737ef1ddf5bd851745c926689c7ef66ef9422704cb2cd3f56a6fe633fedd0718e465d569af8a9374e79cc4100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013657347a8b57e169b4f1c29cf22b57da9dbdd48aefc747bd9b85b1e0000000072d151310278190ab0ecdb026389a816adb9251bc53fa018b3c69f6100000000eb8b3a7be3767c56aff03c5096a17f1c2247871d00000000000000000000000042da3010c4eff15bfbfcd30fa7a00425bdf29822000000000000000000000000f5d9150dd062b46fc695c375598b3969bccf1b55000000000000000000000000dba38e1a0856fc645508ed2e411a1921d29fc02b0000000000000000000000001872bf2298ca614244d8db770683fb0e29b0196b0000000000000000000000004d64ae24ff2fae7066b67c03a0324a7bab48b10a000000000000000000000000895d2c625ed0f63270435449ee8686330dcaec550000000000000000000000007ad8a13b5605e263ebab361247be9b7c477b7c16000000000000000000000000d2471b2debead554ee9421668faf6f46130b61551380531fdab2b21f87382b387aeef00755cab46054d6036efd14e6645ad232691435473dc6237c4b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c966521e43e7af3fc2644a23d77b093c8a4be470d4373223a86f4b6d000000005fd0c75335a6c1547e00f14a911fd205f09287003787410c8d3b9d190000000056432f6f05f2e76c52b76539d4f051638a3ad35d000000000000000000000000807c31744f54a9095597725602292b49dd05120e000000000000000000000000eaddac156fc649352f464e56047c6522fb26ee1800000000000000000000000046c62914d8abc03a5d09f768656a0521ae80b47400000000000000000000000069b7af106dd8987b95019121f5a12f21087db77800000000000000000000000074b9c833d4fd7a3d0d81476e44b8725bed0919540000000000000000000000000d0bcb01829c5059670cf2016b74fe42df0ebe2d000000000000000000000000faffe349fde3691d07d4e12d38e88b74b7b64f6e0000000000000000000000005c0d3c5ccbf7723c16d209179630cf3a41975651e823af549efe427697b4294500beb6434ddb0e0620b4bd2e2531fb65fa3fbf144ef8cb375d35525d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000071f1b3011f97cb6990d76f038641a5009f7b9a26a6bc0066cec78b630000000078d322343cffdb6852f2c90a6bbd275633003f2a7f199616d7a3c4560000000055979271dbb51303220be763d978aa6c96dea94e000000000000000000000000b0e1025d0b13483e93f0e17ee8776957af3627030000000000000000000000001c7bfe2a04bbf07896ec170d06ce990efd461f7e000000000000000000000000977f3d3382e8832489355b5111008f491630a70c000000000000000000000000519a8e42f17a99177205e474b555ad7ede4ca61d000000000000000000000000f531195473896033ff48795c30ca9167b094875a00000000000000000000000070f61c0f8a8cfa2d3514ac3db57ac822c561ef3100000000000000000000000001b8853ff9f7b339824262716666fb1fc461cf470000000000000000000000009dd69626dcb17c11b426243cab8e3a4a69e14114ba0ecf150dffbb056eba712246642f1765b37451ac0ac74869a8bc5534802975f15ad705e34f860300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002688871beb04267fcb3f56bdae4693fe977dd2a3761e860210c9a1900000000dc38a314cf9c400dfa130751afb04e4fbd455b21164e1b068f2bec4000000000528f6c5d646ad465ccc74e771f1ca732bb72b11e0000000000000000000000006105511a9e0249102bdebb0ae8564379e01c0e2400000000000000000000000057c9ce1b4569af3d46b38e75b0705c04cf40066e000000000000000000000000fe027d1561d96d6dc366654be1442c46029b353c000000000000000000000000c01ae60fdb0d1e171a229b5cd590bb08711d1d7c000000000000000000000000c0e03a3fbd4ffd1f5827eb267aa5b5463b32490f000000000000000000000000aa43ac45ee3a172611ac366f720144039c948c7a000000000000000000000000165580784ccbae37f2c66f004694042081cbad150000000000000000000000005c932513bd07e8043d4b9118cba1c25cc674c67a87a17064e6af056c5896c257b14ab50781a3497e94f47556de19ce06978846211512dd2461a9a17d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a65eb310b0cde12cba775166ea2e01336d120e53b398945ddf4f426500000000a2714326c54e29715df02131056004569dca8b2b3a42fd0ef046a61f000000007c1db56f88e25168988242466ef7540b879ba84e0000000000000000000000000edfaa3eab721c4ef94f442caaca9454d85a407300000000000000000000000081443d7b4351ca785830541b6a8a013fc9551f1e0000000000000000000000000fc4116714e7124956f8b618a689d95d805f096a000000000000000000000000730ec36b8477011d11f4ee2937c0a561535df8010000000000000000000000006047ce6ea9da835044f8a254c9906b181fca4b4100000000000000000000000094a17950bfb7ac2ef682296d804b33452d57110e000000000000000000000000835ebb4730be761cc755270e454f256dc767607c0000000000000000000000006c376e00002670188dae9e2616261623e51aef4d48f5b048845ef67156302e7d5e930f70440be00f854a59417d9847102032dd5594da1175186d72180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006551a128eab6e057fdb2b706793d072696698b1f69fabf1c5e06a25f000000005c1a8b41bb94aa29998772046acdb761e92e8b4bde57e3327539994f00000000170f644025dfc3082ec34e4c08ffe531f35586680000000000000000000000008e42944b0532a56abc22974f9ead8734f8e54671000000000000000000000000f0ad98649bef720010378128b4974c4d60448a620000000000000000000000003d68722e63bb3c0e9a4b07035ca64d4605783b6b00000000000000000000000068810a5a035102036f8fc633ea4e084002e3b22b00000000000000000000000026117b615ec3f0141d665002cad1e16971ff4b0c00000000000000000000000090566d0a08ad206641ce340208f2b901df099d1f000000000000000000000000122a311803bcf178494aff5b02ca071885f8043400000000000000000000000057eb851eb115a15f6acdb210d8222a00ae97ef497d8f3a06f0db6e3203db4f1442436836ec64495bfa20fd293eac431a9915da4d4fa083072d7c0b58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000859a823ac76a255a6c220233804c6d45077b4716d968674dd4c9292d00000000d0a60b3e09cccb2ca9ca7a0ad87a9765015a2939cfcd043818d3221e000000006118c51a18ca1b423642f66cd33f740c083550230000000000000000000000002fdc9d4b6cbed43d981b3b5103ac8e0e43847806000000000000000000000000c611705d32f3a95b98186c4c82a9ec2ce93d91590000000000000000000000005e620e0bc996dc471857c273f6669b2e5ad516210000000000000000000000009ced1e13ca1fbe6203bdeb29ad94f54c082f107d000000000000000000000000781e7f3d502c133cad0f8966742ed2279917424800000000000000000000000005dd7848a733162eb6f42260707d1c4956d50243000000000000000000000000e49def65c3fb2c739d3bfd0f0b484555ba0b9623000000000000000000000000290e1314fd5e5f3458f0251010854d03424e83784b611725b71eba227f4c9957ddc6691a8624730aa5242b59739cfb3c5e043b261efd1159cc929412000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000726f6f4a9c0a613ec05b847e3963234a032213447cca84578eb60a3800000000258a8014f406ae62d817032006e97577541f502bc2fe0b20f022c57a000000000d9a0c7e95e9280869ca58426f68f261d78a8148000000000000000000000000a6dd116900c1fa3e4c63c60f61b53f3c27a24128000000000000000000000000fd19c235688a6a6006ca2443f6b8995e7504091a0000000000000000000000004987192b628b0e66a81c0f7cc974c66d86fa3a6b0000000000000000000000009332a91e436e3c2d3c94231b5b85913fc26861500000000000000000000000004055965d8eb23b57fb6aa047a706031f1e884324000000000000000000000000f91e443ce96c6b6b26f7c703f0addc5efa97d5650000000000000000000000000c9a4b5c140f11199beb7e6c956ae11c5d51707d0000000000000000000000008a141b4d96b38d05ff0ffc7db24c464dadd74005f61f9262628ad105881f0e35c6212634e62d33049fbabd6383622231c72e997b45e42329fa54f54b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011af5207bdf06a15715de963ad7cc651a7aa36257610894bc4ed4a5900000000212b2d1ab287554a471dc865bb66302e0bf7381de6956d287cc6057a000000005ad40c550a57562ada8e3357285fe41260bfaf51000000000000000000000000541c9f16f730cf079095f350b9a03230fcfbeb41000000000000000000000000f5a3293d40aedd7744994b391046390f00ab9d5b000000000000000000000000ad2f5437724c1a6c329b494f4de78f1467fc6537000000000000000000000000a490d317ab5f4356c73c66746c93961a1297fb7600000000000000000000000019d8890d04b84c633e94342592be1b46574f22280000000000000000000000001db41b672c00d4124a93667b5143b6254d5f2869000000000000000000000000595f506c678348598397e559302d2b026e7a4630000000000000000000000000bbf4a540d1aaef754def5b2ca5a1cc39b16b9446b07eec7c8355b45334c97031de5b307bef2bbc06df9fce10c0becd71a82e5d3a1e98fa4ef48a9557000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000388b5e269983075c353435767bf68361e6059f1e1eff0e012d24625e0000000087cb43357978471b254c8a6ccceefd6d47d7b178e27ba050bd8c985d00000000a1ab982ded55b8202273bb6f1598b761dd065c0e000000000000000000000000b7c3ea5d39bf825b90d850271073864b62609e1800000000000000000000000013299f179ed2953246e3d20324bd453837224b33000000000000000000000000dc2e970696e7c01498a82f3c3a718d4f09946962000000000000000000000000904545458c58647532511524d656266d95cde50a000000000000000000000000a1486703bdf99f277c0a9846a1485a53272a9742000000000000000000000000f64f5a5a9d932530ed69114a9109d207d35adb7e000000000000000000000000156c754202441822e23dee709c34aa4b98b32c2700000000000000000000000076cca3341853eb5ef3d5707dd1f102038890bd078e437e729a3546173c62d01abc35596274526d27ef97045a297afb1e42a45822744f5116dc9160550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001662681624acd0726018f062b4d3ba45974d0d426a57b64e5a268613000000007ebbc0577be5281ceed3c54636c5180f889f6d10c33953052376843f00000000077e000f6bd2f14199d6ff286e28732f663e7760000000000000000000000000f700a939c4102d4d77e5330342248f17287a1e1500000000000000000000000040f9b25c8a2c651e47dae179c5063f665c8eb8300000000000000000000000008592e75ca617f946e794ee3e3ef9fb3deda1244700000000000000000000000093a45312c6123a240d92ff3886e7142548f2a9050000000000000000000000006dc6a4334143d540de6e5b40af189d0b15a6477900000000000000000000000031322235ac1f714a22c4da697b6fe83bf5e1aa6200000000000000000000000037804555bfef8e78dea5e24296cee436fcc4c278000000000000000000000000d3853d3ce748a41cceae1a3720221e07d04a851725ee463fe936cf138d4692320180220a7461c758017d9b768c9ca11edaa20525e2106d51016bc9630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002218024fd251f84c7b4a64369255eb23abaec12f0d56c53d6ba06c6f000000008979e75e73ba3e515d6df8432891443c8c08911a7d09a75fd40fd32e00000000a3fa687850c7564416aaee00767c9a2a096a297d0000000000000000000000002fb3fb70ea19df61995cfe1b29245e15c4ef900b0000000000000000000000008ee5fc36f8174235fd75a4193af07274ef82f6390000000000000000000000004090002a23e52e6ad6fd6547c99c603e255b737a000000000000000000000000fa7fed0fd14f070f8e71523c99d3dc100b1009620000000000000000000000000198987cdfccdc26d6c3196ecdb2dd69010d782f000000000000000000000000ef7ce46681d4355a1b99f32d2a21d528b6316b61000000000000000000000000b725e72cd922613ae8fae175c1c53c5764864d4600000000000000000000000006d357013fa146439bcd8050b845034f2b2ad906d52cc60e84d8b71eaf746911cb54201ea0bf7e674571e67b23aece09cbe7a72f08afb805bf19876b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2c2ff721c2172253da3383763b5123ef9226773ae725d5eb6b770460000000081dcad56b298770e2ef33355e30c3927e2b6667eb8e61079943de47600000000c9971a4b1575f624b848452de7e9011dde5e347500000000000000000000000006e3fd63ea48f471dd25735b48481a3db83d262a000000000000000000000000fe24681b925f1b6219ab8443061f4245a59f15280000000000000000000000003636831285175b332f0b4f78eb099e22760d3468000000000000000000000000974e456d4dc3151ddd11f4707a254962f12b143d0000000000000000000000004d82e4131dd8cd2f09447073626d6a612631f626000000000000000000000000f7d81f5133b296001acc3405373e2b77a05a073600000000000000000000000012d49c67ef09641a79cfe41e13532e49f2f2d243000000000000000000000000ba73683d3aeeab083834e015c3a2d04ba7ae8876d3a66634ea70522bb22c724c9ded353ddab1a85ee3b1043b1fc4bc59cdb97a048d9ff60e97f01945000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cade461e357fb04d42918d1dd671e23bf53b7f169ee4190095925c3c0000000024783f7aa922a778ad2001339097201cb78c395a502973353756dd2300000000e97c076ffb983c06d09c1d29936acc117522a81a00000000000000000000000007ae2d0b955075629f2acb08e3b07b188a569e0a00000000000000000000000074978d543d80961540bce72e505c5f0c5af3440c0000000000000000000000002696f9758eb94a6ce59bf305378de40d8b5d7004000000000000000000000000a28d9820a084986b6adcec68319a30106e3c20530000000000000000000000009936e3639c52e26fa074ea6b726e050b30a8a0120000000000000000000000000a465548d404156c672c9b3bee63f90b04cbf37c0000000000000000000000003a2c9349dab0507448c4123209dcb71b92ae211800000000000000000000000061005a4f3f05ed7b2fa5531b1bea0e5592f80032066390035a3a37198fdcf54bd5efb93648dd9274c92662205ef6f71d01c4e05879b99c625659d23d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099bcbe3547b0074893787e224a81cf70668d4c35d94a495aadce113e0000000023babd09428b886637f0a6243887314a9a9f363fde45c805ee29190b00000000c9e1e74ad36db87e46bdcd0c9fa5ed62a7cd276800000000000000000000000044a9565913652131c7dd094d2042022e29f7ac750000000000000000000000006750b4282a16307bdb05035aa7b6940b2a27cb4600000000000000000000000079899540b346a41c784eb634774d891efff627640000000000000000000000004def5762942fa73dbd6d793a653cac0cde479d5a0000000000000000000000000ad7752de694313f35ae2139f6193460922c83430000000000000000000000004b9b7b38bce38d5f341bbb4d034c0a4bb12d2846000000000000000000000000577b242083c1e00a27beeb70a0cf493c45a1c76a000000000000000000000000fef5563db11a636550cb580b66dfca4393c4f96c5d11ca75bb60f07b10c7f32707ebfc0acc95a6223a1ef46d81dd0748f5fc7f1c7bdc33454abc275500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067c9c8245822740659d32460d28eef6048bd62551bca5921f2deb154000000009910e821ff547d5a4745757a3386dd22f0866a5c153b435ec62dba6f000000006bd4d072de3ff30cbbd86f203df8db4d5a408605000000000000000000000000f5b91136ab666b7bfec11338a1ba07323d376f440000000000000000000000001df1992750bc7d41f951d6356d171426f310c841000000000000000000000000128e00331341e4699767012cbc3af20ff1fb2f71000000000000000000000000f6f06f1074d4d0237130bc68a30d01158b69fb110000000000000000000000004ade2a31525b9005fe4eca206b829120a3e045170000000000000000000000005b545a074526b8390f8a5e0eb2869a2bc9eb7378000000000000000000000000e1915772bbad3f555cc8583f1acbaa32ba8c36630000000000000000000000000ed9b56e96ea7c74c382cc4af04bf614b4f60169a5edd8436968565053a8a82bd7a6dd331606273ebf54f3240f6b4b45bab0994ec059d05700c12225000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea67bb5a0b8b252e20e7ab34516450173678717cd974ed43d80b4212c4ff6f6108d69e788e5a1a6bae07af2a32c8be1d93fe727ec524d0565a03443058fc502c9b9566164afaa9648244841c1f6df50323b5b31115580e7a69e15a3c3d2a2376335cd55ed9d9c6382d3032098bd46c7e2efcdf5d5eff7747e3cb9a1e490030335186f849a2fb681fbf1f8c694a991a2ec169f3058f87966c503f1d4af3eee020df69a34566a0ec40cd710b7011ee201fdf0ee27a536b1211a5bffa18a3db9a7e1351582826576625fe54d5308160b252ad150c680d575e35360d154ad8b198178c29466582d8cb74da299265df222e3192d69a42fa3548722e89c256fdfc757cfa0051665cbfbe0b72e861535beb4e352c5b300b1d5cfb6756900a396da621419415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3adffc592d5367bf7cdfd33e174f77b4107aab9a47466dd9211bc58b6eafacd15ca89af807100dbd648a568a60649b6d022385a228a1e381395a4a203cb0360e2dd5e80e69c13c501166c6df002a44ad006d33076c57b2b57362e88f68471e9b43549d7935d3097e4767d01042c290564dcab9d504e2a1fc263cc5d1423c208e1967654250a517a750c423d57ca226bc72ff53615dcacdce42da62c547dce5a23c260f097dd3f64948d812ee4317a2570bf60082183df567494c4fb05156ebed3827b44122b97b836c0b6ea86343a0b85f7a871010f7b4387ee556755f371d90354c2b5323aa67eb1d560efa0fd1b37a5aa93fdf1297ce9f4d8d753b045ee77f0451f6b4392b1fa628cdf08f4631e7ed4ac01b690963369a43f1ea9c12ee3c2116dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a502b05571e92fbe46ae1ed731a21b37759bb23f87667026660da16dd071575ff06e2d20168f1271e46bf35a74c46202108bc7e645d6db365060a307a4ae1003d20dd2f484e6e6e2d663d16c14def8f6a76a2ab78452e24cd4eb2a29338b608a475f271f327e31f1034da303356cf1bfb17a768dc13f1cfd37ab5412412bfc7663a5335405ae5dfe54d3ed3b9575696775c8ae07d0d2468fa53581b5254acc3620699c16b3287c18f0d09a66a6fee1d0409351cdd3582097a4b9eea6600bc34d90f6625281e0ac19324f822fa3a9b410c246953b55b636e741686d66a402b677a42f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a791d715fe591767205b5375294a6ca2575d55f16bbd27439c54af023cde546175184686f5ffa27025da7827c95b0d2089c26976033110630e28fb153f91eae1d0fbec4462f3f0231a92c874b1a7bb80366cf0838ce9b8d0e40051d4598bccc5ea1b68634e1cf5943969a223653fb3c215be7aa7e5af0ef6a9c1b406830c32526e1ccbd62c7035a4418cf2f5a1bcd7a10dcb879771b6a226b4d9e1e1db0814a166eed6b03efc5c15137d2192358edf1117a301938eb521f5d060bfb652d8bf55f8bbe714fdf7fc12b95b1144cfc47146148fdf263cf3887070d2c3a3352d101387b45f343f5dd302ac49976780a87d321b2c1af6d9e3a175a81848a48afa2602afc9f355f5dbab711efdb7e74074ab91d5c45674e90939f762b2df2706a5ba52e19320c5c0d3c0075150f1631df546e3f34589130592724425836a16c4947037757c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5039d21f393a24b21ec78e70754ee3e15a533cfa34fec55627459e325aba953d1395f0b044d001562fa72437296b3b1966b91c0770d44e88716156de053aa6e02109a0b13c84d9b937f7584c6564861f4c29666e43f83ffd65bec69b7bf91e141d3ae9bb40149dc57464f1ce3a6e04d401d4b2284a7d322e53a4e5b55df9982077b10fba002be44f71fe8e922ee135121dfc39a400af90452a3f852a606010965fe1a80d3b394ca52f3f8b7365e7429911b7821d760f63c1725f055e5391d8c216b003833e16da0c677bc6340892a1c264c75c6f62ef0bbf4bde3c102d43cf4104ef53db265aafed23b7e40a614abb1b072672b5778dbe5e6eb551a46aa33b0b021c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a6b47d6c1043d36ea9c5616223cff1297dbce277777f36424f42406e54e0aa0322384b60937755296fd0ba71ecb17874b1918e7b28f16a736b66510a631d352b31b10c779924ec6800d82340daf42c74409233077aa4db19425d614cfdf4d5281c1c541d9c46b12b29a79d7dcd98231d8be10946dd73a7029b3bc51f33ef8e139161761ce6cff47afff0280d7591890d5c6bb7151e1c6a1c1d76cf17e806a750f830e0157413a047d4a0e204eeac024eb1fdfb08b88c2926844a8666e6da3136e0f56f2c861788052498726fd6a4bd3a51f2601a62fc1364c7d36f065158f97c7d08f65787d9d6381126c207e43c5e22a2b86376d1fa096bae03f343e1ddb513b1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af688da7571bdac4749d4417759a6032b9de9761a55e6cf35d4176f60dff1953992f2d04e3beb66389696ae02081f7e555fae3c05f16bed0a5221ce156aa5c73a2d68325b304bca4a475a0a5c2e9032532a7313342b54b432aacdce44823e276d0033ee432bbe907414ebf76e992f2f624e1a035353fdd43fafc63826e28bb03552804d7ad8535b7453fd122912d08e3f643f5d58d1c31a4ecfa14a4dd5175c479fe6443c22ea8a7041d53434638fd251477d56782ba10b577114870340bd2014bbe0142010bcae65c2a160016dfcb03b79c1543b2d6c04349b344a643233a91b92b0a334ee7cb94179b7bc24583ddf753973712e20dbe604ddf8df155aa3960304f8d362cd0fc412a749c15813b37e3a08f91c51302fe449ebcc136214369c2140d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505a3513760956db4ff6a9085c4e96be14cd07824fb648b95027c15202e3422146234d40668bcdd07529bbd9609d170f10b3827b5cac4a1734459c2b41fd7e9e3f793389318dc109799a952f4f53655b0b58ba49076d850e249fd29e13a44ef873cee174653c4a7e7361e1524f923c2409f7f9705dd0ebf1390758bc3d03ce266375ba044f22bace711df0982adedd31502d148b7ab07d027b29f26e57acc0321bfa68a62a7358b834afdf251adbfb1706457d3765f36a7934cdc0eb68e1fe0862c8087869b4a33e5c59929d0c8ae966442774e8236e588905ca40755e6c25ed0a25233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50ba90a94e16b90d107fcf747ecc0b8421db3ec715057b1610f6973d78fd90cb08731e5a103c32ff060730ba78f2734f0403b761470a067a2a7cde766ef926ea1fac0cf273a56e9b6972004d43e5afc55e8ddd192eb688a200e6d15c74f19b542a803b282a69e24571dc5a375d1252666a59fc8836b8a9297e5f6f4566ca3f32664d20891c5899217ba57148353e51fa685713236ffa70b921b92bf7375ce0796682e3db74c770b9236197dd1e46da6536f38f6773ddfb581168ed78203686d05b3ea73444452864750096120f1991dd629d29196e21f7c66f4bb5241c0cd38352e18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50042ce80397dea37792fce75a83fd0429dbe5fc2066bd7040327f9e2a7022dc37f730ba30af0efd122f9bb34c1d77456ad874d46e0ece7718a4452e6abc1b344ca5804a5fc034aa3f3d6d0f2d9da2024f2048b470eb798f7a0561eb69d1418708284262119c46e8487406875206d81c64360c12316e613413f10ca722a923265bbaac1016c98a0f1993937c23fef12b35902fec1904d3eb7341918543ec019944ba1c9c1dc625ca04ec90114ba9420279d5e0c15021e50971042c5f177e3141121e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5068f55e0a05dc755dea8c8f005136615388ebdd168c8d0f2fa1267a4a0e64c97a3e1969355172260a876ccb30f7f7a14123b0234e1edca051d51590775dd60a1986db5a36cc4c0349d0204e6c279935132727762530bc5b32c19c673b05dafe76837b2868647067152ee7985c82cbc4775e9471773179e6282e5cf95faa2616073ffaeb644f47e06a3ffe904173036b2d3bc72c55b7e4162078469a6e8b988a77aeb3d779bb91e31b95cc6e71e0768b2013d76170db5ea956ee809f3771506a0df2a8804e8104ae35f3ab431b56caca17895de927c7cd9c1fdf2a8d290c99814a9699331ccef5675078c4b96376d3e0244ca5725e7dcaa56fa72072242e4fbc25e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501e49b90985e72b237bd4ef1f0ab384145753e9192be24611a668b43d59a15361349edb6926c1344620035953262cd06ddc0d9857b98397240dcb81193cf73337f214600f71cf73161824240bc0fe355a2921fb102ffb890c7dadf06e4fd23b199fc6af63c837d84e94e46e1a6cd59154885fca0894184f497a497c4402c042448f75e845289fcb09da9c3e025da188166b7a1854d2513b03a251af75f01a235b6e4a726940df9b0b94d4102045585741ab011210bb9f5764288b7d350798da0cc2902265c096ff509c51b9203500fd2bf3d7313a54d33760c438bf1e30963554db80e7083314c214b8a0e31a9c470a2375a4071fb7e73e6bbb8d684335bcc158a413b26706205e19db4a665b4c6d14315baacf452498783966e7167c982177462aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a69ab2d01087989599b914573824aad21170f4e167c0da639361b5e41513d8c7b43824a1325c45f304b52025ef1ce9549b9661f2d34f64a5546e31f1afe84e17c9ad3f7571dea711aa68d495cbdf5ea25b9219c215cc8b3207c078306bc21c1725a8f7322e34113273fd8e92e8864553c2547390a3d1c3a5c70b2a62907874b5bf21c3b4f22e4315a7ac90134dd60e06d77d4b5143b2f1f4c5a481f1c8a7c74789c4cfe10008b0e67f0a755746e2d5c311cdf49050eefcf380ae9306e73b78b624d64a53281ad1500447e993d2331266985a1e0460963142523fb572a562a3c63ef53db265aafed23b7e40a614abb1b072672b5778dbe5e6eb551a46aa33b0b021c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5072292660252cfd5a86c852152e45c00804893d39f1ccfb0ae3e8ce0a8882866b95d41476199d35216af75861994d1323ab40a615aeea4c1dfbdf27077906ab1a51336932eceb0e2d93b552457d429e165e32db023a0acd1a77200d0d23b4520ab5654b2c7375b400eb73592c6baaf9494b2b6379622378546761bd17792a843aff04e74d423ace4a4ef7497025d8656a16368f4167a73d4665662a52d3c0c642d7a4766af187af79f2874d3871548c2afb5fe279d57b9e77c8fcb93307e9945aed172e49672c7029ebcce907f48ec50ada6f4205d163b71fb2c0282475316a43b7007d5767222808c954ac70815e41758bb6f570a0b80452deb77336f94a4d342e56ac0b6fc9aa182a73036ab0beaf47d0d97f4a123cdc3a8738b86dd2e047427431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a608b2904c99f1a3d4a51136f2744cb17a8de94710bd87244e09c740da4521d08a86eed56d9415a7df53e86382f31985d9a4ad85386f7d52e638328680894ee2fff978b51f6c62121ecb89a6254abcb407068a358716c443f55774c3780256434cb0abc7664353a314be27665571f39024176a66b87dc174a979a6513336acd1eaad0d2383a09e545d8e33a4ef090c9544c07fc2447dcde5c0ff7397bd105297a07688d64fba3cd3b16f12b5219da48007f189735bc73ce15616d98136b35ab2de160c55aed59981b09086b188ffcfa596eadeb273b9e790258e639790c21541dc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a5796a572b6f8535a337422740359e7b855273642c543f00fc363a38b57c0f677f7dfa2cf5efc56c399ef1494a80f25556cc916184c9ec2846606167c6f315421a7e0a6ca8238d524790d658469b0b65fcb00931b2c3bc25cdb80b1edf11d55f3903e02f1b10b527a879b84104ba1e30f7279d766d68504736394563fe41904abc0c137c6120d424ae0a457241715f25dc4515186d17692b06d2d71992fbfe40e1dcb8334bb81142471dd5449b56522bec028b6f12e8710765de070a658cbf3077e6c1097f1f1f55d2374b7baae8cc3ec7ac285f2f85c336a8a67d7d43b9ae0051027c219e7f7f50a9ded4586953bf4fe319507d0a6ed44f28379c6eed86dc6742fcef6c8818730794e6562698c224538b60ca287b5480608aaf15664443247a70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50816cfe2d2749a172edcb211f578acd6184d1562047277c0ac91a5d5434d0d92108cd407087cc4d47ff666f625672f76f56c43548ad7fd676316deb78118a0f53f9ee806875ae6532ac210e4fa1e9e32de7bce3492a8916637fc1e00f36608a14db8b8c5308759857ac38510d78d5f97bc1ff6f4340347e075f1dc02cd32123409c0d974c576ddd65bab76d505dd1933377d27f0d7e5cf75222eb5b68739a6303efd5dd773fabe377ef813c189537f72ee8f6df5079bc32553ce766372a1e063b6213e42bca182f3ed167b71affa4511659eff469c1cc0f62191aff6bf75bf743340a843391cc67266afa64201f4eb90d6ec1c407a8a60d4876944871bcf72137e1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a29cc1f77d4f3e173bb93c27341b8d90ec3165d41a1ba316db133bb16c30db8254913aa081c709a00b22625568c52d526faa0de2524149e0f054b2848dc254d50d9ad26518c13d44b8ae8b32aa8c476304ccaf823bf39f25404149d2331671f6edcd4db455365c10a1ef3e878d0a2543ea190ca6b8fbb7b40b0df8f2621edc72fdc60ba2309c3b73c0517d740ab680370d160d3125748d6383a0b8019830b2f26e0b6105b09db0450d8755b266cce2476c6147a0756079d6c3702a345e2518676e990453a67583e0dfb93db5bd1ee57437ba91a361a59c4150c1e1a2ea03c9f00e6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a6bb75704c6b3556c3f0fee7407533a02844bdb37d3a34b6e9fbbb65fab165b42bc8b245d5320395fefa6032b4183aa0c6011416a439fd0351458520867fb361905f00256a77a473d9ce9614b23b998553aed65247ece763af542b65a067e0d20ff12f2401c156150f908dd2ad6ab0e346c1bdb54b360fa7d1bfd173bcc76b74f25173e348d175f1a3c9a3704bcc69870c2d96c39ce9e986535dce752be1485430ef53b28648e9839c4845e1da3e59d40b41c9e443f29565ea5b5f54181f37075f3e17f326cdc793dd15a7b381da6fa5fc29a7d3e1455333ece942710fa4ea8714f82973f15a5c65e191c680bbd1a00237949457ccc1690461a84ac164fb4a42e82c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508fbf0a38ff0e15160ec8b33b2f1c11356b17ec5b71c6e04cbb31aa7e15d9bb7e515a25233b5f6e155aa83d13eb88065dd0fc3442584d9249c313460851f3aa1216bbaa150bf0f7585626251fa9382a74aa74d102d214ad49e75cd03e3d7fdc04d4c76d3fc79ad07d3dc3165d8cbd2178b1e8a15c00d7672f33d41e7d54000b47e343be4344faae63e1b26f605a26674a100b3410f5a2f62114f1a017312dc900c2de89374fd6fc66158be23935db803985fd5b6d70d37e1f6cd4af7e0a05627c8ca1f762aca8655bf5227b7bb17ccd6f55004e77cf1d066314db6824c21c941b1cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50eda98b3f6a6a63477e7dd83b3b23ae2904945928f042a52dff2d822555822d4d1a64a07611470c26d630d33d4117ab2e75b08f7bbce9386e7cd2b04c23e7c858a2fb042825f0215af860a951ebe5970dd56d080705f4eb19a04b031b7d08e3481b401a5c23d75434ef61dd7d265d8731862c21519c5af077f8711f0ffbe1536014a71f2f9df5a11407c75b10d55fb4132aaab93574637347cc3d7c5569772a71b0bbe74e7354da64f903e815ce05e26f12648426d863eb30fc75d84de0cf8906112d3e4e9d022912876d6345253a1679c1f2515151952e470ad0bb4883d00d4e3bd5eb407486ad474dc257596bba07608da43f709fa11902e86d025544cea82c081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50fcc190505b83795e01f7d93658e431481705d82844e26e1b1a294a588e47584730ca7d679386ce6b7303742c6a875866d71d007b9b60c3348602b87589365e0fc63a1c3ac71b0f26e649c705fa8c0d5acd5d3e19b3da446054e9e203703db54d209ca30bcd287514605f8e09da532201b4f2944a98704104516d9666b3863a37fc6a4e0b0a92b867d4a44d25f1199f6a8003813ff0ff4c0199cd2226f74e2801b120c166d0c51646f2b6ca55b625a31b32ffec764e3832514d471e2762b4c52af2a7655350aaf55dd9fb542ea8672060cb700e6bedc9c64e397d1d18b989eb7ce18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505532f7033d7c22456beb005be596682be0a4fe19ee463d198dba307742b0b22d2865701455dbac79a2eeec73a870242d3769621d5c82b627b4997d3e3aa01049af9be82a33cd174f172cb41e0e1b2651b06b1172e529ea267bb1f2205d345729bbd4b16fa34f7011e8138d24f7498450451ae64a9355a16f82f7cc05e2de3e479b298f5d5c16c86cc16c5d7a153cb006638a98641f7e3c2c8d0a085b7d22e94972b2d042c006e562933150445e1da948a612a90d4ccbbb6df653583ca840215019347d5f95f3cb0dfef3a31f77e980192d658238ab969d17a5e1f1641fd3de1733216865d0a5c37e4796bd317802da2d10f48f59a8cb010ea2b2781467237f0548111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e12cde6f9c14c9468028b36b1de50c1767cbe0189890f76b7a7d823f7e331f664d0d7e6bb299d56ee189b808de66a4477b58a8202787274dc6e9252989c90f744e4f6a4e6a1ddb5154720e42c9f76e018fe69a09bd82ce264ba1ba35552d6f3d3da6863af65f0860420ba42fcc375c0cadb4de697fcb30580c865e43c0c3b626ca59d136d18ca11c4b484816efba9d426a66b26adf76d47a33c07d4b260af20d721ed6632e586a5ce14483104fcd3e2ea7ef56463cf520761786aa063a466448ea6220126ff6057d68a71a1af9a8890bac53f23ab7a33b442f4f2b540e89bc0e28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5078480734e847c10516dcd4011f0c017e5fd3514c6838880248b2d807e7a8ea5f1cec724866eac57db968267e6585de27533ac73c8a642934c029a464e7fe2c3b32d488264b9b824a51ad58454f53d27edac0af43244e866f83959406f21f242c6aa45b404934690dce6afc149e996b54a02ac80264600d53f4a6813e2dba257ebd01a64b6e5a815b2dbd9321d4146669e0a4301cf9ecfe1f1d3b8a023bcf225d1b0f2c4f0f8cb4785f21b06ac3420e51d9853a74b3e92468c47e4144963e511cfcfa32304059786e05492e204be9a607206d7b51986d653b78a4005d45e0c37be6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a2677ab3acb74a53c40b8cf49d049b53a0d21816fee0c8450d1e4dc52e3a05f42fe66a87d408db60f560ba469cb51b020b56b9256c867f158f0764d661cf35f077918ff724ad1c0359e59d51f29e8496ef56b6d3a5b34ed60702e84083ee3c367c038b615b2b333757d81d305226a7b153057e81474726d63f5c6d4392be4926ed98a960f142b6f423a12d472fbb5b6706e786e45a57286437f1bfa1a5629d104b84af545c3d9b837cd06a75d3526453cc0d2a26ca76ac63576869a7117b5492c93cacb084830fb23eee96748e1b12a3624fb7d1da0c8b309d7bd78128240026ebbdff93209b8df6092726237a70e645aac7c270b88da415f3db7b140b02cbb5ffa1d332f00290d42c677913bc0ffa22513c90040ce317638b9a21535e4dc82112aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a32a1496198a1ed5962bcca4844768a1ebb2d083bf35b48377d793b39733b4334311c30143172c649b4c46a248458a00736a6a559a32d03354ffc533591306f571b70250882e56a263fc03a7e5332c26363b3f2290f750e20588aed72e110934e6da4762ec9cff76e051318490e5d261cda09a76428fc587a79ae75319774c46d027a483b66d93511dbcd420680dac24f0b42d765c00ddc68a9edd639ddc02f1ce31c27196a81e31b4ace1e42452a0b72923e8a24e29c2a3f26b5a033c50ee76ab4979f35ebf598189968de7c3509fc4d1a63e459a9f2cd6fde5fa9349a5e65608c29466582d8cb74da299265df222e3192d69a42fa3548722e89c256fdfc757cfa0051665cbfbe0b72e861535beb4e352c5b300b1d5cfb6756900a396da621419415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ad6559b23674935358c935738188af9713cf21257b0a2e00501c787309409bc3c87876636fda4eb29a97188077e5785075433055bcc87455026728069476ade06c221f234e4f44e2b89b7b449ff3499400ef3f627c54b5700587ce43dfd12774804e69e15f50932606b9dd525ee280a407c30ed6f6349ff46d6bcca588534d75627cd7b10df8c6c493647427734e6b603fb4db3417696da619209c13e55382701b9c7686de4e72118278c2f2ffb36d7762f0403018366f964d5954a0b70278c3b301ebb103502632b13d6a3580d77133a0cd4481f2cc6d94dc929db44f85a637c2576560d1a480966a27b95701f26c85bc2ef7c0fe50a537dd6776f06eb4b9f421a5a640e49da0065987cea66dcefbb281e76893ef4516b0498a15e4f0a603f55f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a3123d57d20fc1f3b1879f30731ce0f3b628f29751bbec31273be4475ffd2bb4ba9ddf1057929056b6cb03420cbd4791575c9122367b317349063f20261307a0d49ab721d8f0c8e6d05813f634366de2b2457f656dbf42a3e5b21d83b3395973151a8120e8f0b4628edb83f398bbba11f0751ed7ee03351622ff1522ddd348135cc511753cca76f4e56a04c495c1d421f232db651efeac65c8135d2636e5c7f2536297654a3ddad0d1cafc01c66f5a0492800206133ad51541689b96e1f681f6e837c6263e69b4b00f09f41282e1bab0fc4a8cb0fb8e7e264097f2458c528a21953f98c140d786117906f216077eafa482c44d04be32bb04ada38390205f46377115c643658d9df1267200a3fd051ab24eb92987837ea0a3dab6353707f9a094b70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e8c29919c36dcd3a014d8f6c2700de1469b7075571880e58d8ed5f3aebd0cc4e4a8769231bdd9e3238566e4d7dfff50da60b654df275070e87882902636522511f7db7117c0143798fe4854b4d01635003c356692b847d343ce38537d57b7e706989677d833b1662e07aff36a5d30659a8304327894b3232bb639041b4d64428cdf94c478fb3cb7b2415897068616b20ab7e4a73fe395a7d34eaf9029c4a2641e05cad053687116ddb307c4cb78ce823f1ede050f09d99456341f441ae4e6d6d7c9efc4c321cd1476e146f408e4d5d149d267e35f73e4c4ceec72f6ddb673935c6dfef3f9effa7047d4086558ca33f5e184dc0129198a44460575403a06c734004f8d362cd0fc412a749c15813b37e3a08f91c51302fe449ebcc136214369c2140d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b7d3135228c0812dedcfdc53dd6e656385ad803c9ad3ee16fd7c307113a25803884d5f423acffd547c2d5f11a11e121b48efdd31783a76428ad4637ab235a53a2832ea4163d7c4219f57d63edb99f3404d5e68170fa36771271899019083ff0b94d0596b914cc708a1f7f75f540fb6050c1e2c54d35eb678a3b5cc51e6ddce60e2327807e59b4c14ae76445157c53a259d76204486704c427c27cb17e2479b68d0b71c6ed9113b68b5f2404e291cfb50e877117199ce035725a1e04a0383e455002e9474a126052db9c12919a684f0284d6bd76bbdacbd575bc9401e6ff0096d7b45f343f5dd302ac49976780a87d321b2c1af6d9e3a175a81848a48afa2602afc9f355f5dbab711efdb7e74074ab91d5c45674e90939f762b2df2706a5ba52e19320c5c0d3c0075150f1631df546e3f34589130592724425836a16c4947037757c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b61ae9551ec80918a5a47b18c5ac204f1503615eb0251e302075657795e8f058171217134999a92fa357d32d75a48a78cabc0a19aee008450f09a17cb84ae47d781b6135a9fb032fddd3dc18822c616f3d60be104423c05c8a92be6f8a814429f58f1c05e3583023e428863f0bf9b03789e98a0a9b851f51f14dcd3ba1e1480bbb0c0d18f745a805cf72c85d06dabc3ec17b887660c88c451fc4fe64b594916fbfab330223382929588fdb075282876f906b562e92cf0f41c7449f4f786c790493befc2fc4a44736f4e91e301aef365977126c57b4084e506ef552274a91b03880c9a27c1a733655f2438a7d3b0fa138326c562f528980304ca13f45eb112b4fd2052b4440c87869f276874faae1037ab44d086b58451f6342334976500e9e70152d850c92ac5d2d4526305abc8764060d9f5049b98a192bbc5b2d76a294d03fab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af6592e0d512dfd71c5a0461b73ee794f440e137de6e1090c488b913f44b70d4652aa1357c4b4424d0952d64845ce014088a0b30624c3d5617fa80e552db4b76e3aa3580f79a363150988310b992d584972c8745c94319e63dd856b4a8d0ad71739b386595e76ae283d04e15b2472586b76f3556f3dd2e72a8305f67b000e076b87c340607b30da1cce4696686cff98365902ec310b1f38010c3611652fee63591a35452dfdf20a707049800f0090f002d7243a279852a624e4f59075e91ed8352a4db75b08c4c4629b46bd78f10771123ff5977316d40d5a7d4fc46e1930851d9c5c9c2a6f308d0a553cab21713a6276aee8466042c543267461294e2978f720429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa25e5f33c6851074a210ab60df796d53d71d56521917ae289f265f138f1e9d0d78ec931937cd0673250ec4583263b62ad716665809689102d384af7169deb87058ce971f0e1a7b7dd9bba63e1864ba37f69c4c48ca5e973fbe99d7216594d43ec5febb7793f9db48ae9a090a0e4f832c0e985c19d0a87652729b940081534a3687e38313ae4ad4226cb4e355903d6a4a98d52e69fa2968167041b60dc2057275ca4b0f256264f56b425f5f3bade3f74ae8cb2516067f9f29306bda721c909d054675fc129911a916638a0c23f1acdd1ab551427d93299e69342162689b05316368d36315e1bb8e5b115fc22c85dfb37380663851c388a35a612d55534c57c3692bd60471f63b685741781c372722033dee37ba533f1d1f0b13551404e1924a3361d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a682f5b41faf87c2b54d48027b9bfd027930c1f5daeb77b07a91b9f4dced8fe305213561c2ce6e276340a0e5cb2337b38d75d83750e2b71070e3445758f7ef3201ad3215e9a2c9a3ae5a27a4a92795f0dd50d6a33aa1869380e9074217e7fef1101e089060e299928fe7df925c9fea06dcea8fe2b8de46e50f055ab046861210998d12b3b017917021f09ab3f32fd6f49d3cde77c1e746a24a0c1ce2c1f5ba054b8a04817c752444144ae4b22ad2dc6458490ec707d89b953b0482f4f81c6661c90391a0d366ad0316944f87ad527d019cbee04524166ae69f8e31639f78a10785caed3765775ae00763c3066acef456150afc03d83d97962bcfeff3710452a6ce1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ac7bd0d1ecbd81576036eda25b85b33714c86d27b3577e512c76ecc5e207571724a1fd600b038fc0d489191006db2602640f31710cd27fc303d602c431cf3923a692b8c44ac395d3467e4bd4addd3395927661501ed362e51f3c48e42b197734846d5ed1cfb5f6265a1d8a14d23f42508f098656828dbbc33c7dcb44bcefd0d1f01af406783bda13ec3e11c75a171f4396978da122ba7dc496bc0b00e244c935673bd5d2ab47a9203481c143cd1cb8b720133f96e53bdb35f8a55e660039c3d087f91ca75395a21587962143d881f56470102715b32529035eaaf23263c16e6148650985d48faa71f1068265944c1d879fcc13017087f5936b3c9ad0ed209a728761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa21be94a02c3a7044ef54f2db2873637e7dcfb0cef5581257d76c23414332251f307a04d799f575b39cc7c5a8d0d1739c1fe364ac85f8a22b15e0d511cca985a9cdedb64a3a1a01b3cda4f68163c5c12d9bb054b7fd0c053eecfbe264829d8064c2b9700a46b273c56e4ec23130fbc749184ac2a5d38e31088ea5038b8d5cc0987a31b070c9d6c2ba44ca4683656084e23ca5a0c34b359646c2f861c9c596f4104c8a559f02dfe4461a1b81f9681e73e8d7adb0d51479838877eae751251ac7b7ca2a930c0ac6f081f843d5cb49dd20f4634a63e0e423c37d24f68501c8616483acfea6f932d3b1fe2f8770528ba005894dff014187a3b4bb051786beaefe81429d33c6b3c6103444d1d5466490abe196908f3334b0ac01be888a73d01026b14d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50d5b392005a992c1457d1c3104e146270e41652653eb9e7537b774f3d9928b633dac569627d1aa10635427942add92b2b92d7f91948dcb1548ae193315589dd6f42585b5945a37d664b991c07a117760f558de226e4b1f5092768e53a3659653cf99d0b2b236358642f9d956fe66d2d58f5324238b35666660f987f5ba09e95669215734e2c5764278af398210d537e4bfd83903f92dfa80a4573b923f0395469a5958c5f62af56053e879c721410c11568d9095acd574d071dedbf5ff8baf737e990453a67583e0dfb93db5bd1ee57437ba91a361a59c4150c1e1a2ea03c9f00e6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ab3242670ddbbc67b64a32a40a43bb23aac58710a79df5b0f14085b5ff74f2479b408ae5a97b8d104414f9a577d5f8e2c77a98600e8c76c346f1e405d0d52837305ad2c0fcc38b47285688a45959b6300f5ca842822c74239c4114b783261b80e23c9a57ed0c3567e10a33d3df47f5b6cb2ae1f0d2057cc1dd4184971a3bfa864539f8151f0451b293c55d2083b26ec208da4ee7599f14873f604ef70628ea207463b430ce2447a572f18f304b03bb448e2562f715cc138179f689c029fe4c139e11cac36f9f13807d013307a879853775f651f2e586fbf18edd8c802ccb36c473c772e5fef3d3764846a6a4c478a3878ca6bfb1bc8da037154943542a8c1556563ed0361bcdfa906ea7a3b4ddaf17728cccd9c29f1226e4cfee6d56962ddc62321a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50788aee75572297634b2af4107275e400ee9ab240ec70300314f96d460883c86273a7d62635db4f553b7170414ea6f941ba1f7b7881b7756c2bea80588de0762e11c1f1220de60067a03fda51233f830e30ea186a6a593a0c85b6c52da326ac3fc57a5b23d85fdd068e49406dc604230205d6d762b6076e4788185c00974e1a4589d26835bb2adf181145b63c4854804156d833008a73f43c5088916f747abc5dafb18e1111ecb16c247da37a667c7f308f67df7587cf545eacb662779cabd5750ffb003e1cbd061fd7e80c5ae3b2ba4bde33f53e3bd1391875ad42468e65b4686d1c7b704ad8e8049da5f95ddcd4f342eb8d6062ea6d2155515b8f7b99d4844f081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503041676c589559021f901c10a217ef64e3c7ba49c41900516a6a666ca00e3e0e5a07ee77ee78051e44da0e47eb5879184345916296cdc6153dff630cfb40752ec39e914b6698a44511106d13d1c6ec2f116b4b5b8011a045cdebfd5645b37c3cb76a5817e748290cc4c793408351302a0b6cbe5e59f4274aad0d3f4d0a203c337efcd63d43ea0a3c5e8e8b1abc4bbe3ec411ca640e1a52635ab65254c5fee2003f991d2806b1fe7027fba6057d22da788c2ad166e4a92c385ec7ad531cf393302dee7d6d97f60c3b38af7d3bf7720944f437fb4195cf2d5df63ec35fa50dd725a8ee8b0e2050494b0e5f23338a61f35d775ad91100b99c6a9eea85311dbbfc5c762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acaa070269cd14c2d49293629a526d9604c2e13673b700d5306140365fa68b52abea27855d0224f577222b73efd6c337423bd0b108026fc6c1a77aa75907b5941002fca074965762e6fe87a70c5e29b75a4c54925c25dd83a46892f297ef3c50680349b72d658b8074c39da6f69c0c03f4525354de934951be584da4ba343cd11d43962697cbb1018c2f67731960be716878d9c1c3ce0d258e332c2467d77e448331237136934110568172b40599c95564b18032bf9a875694f93384b97e4d41f1536ce168780714e591d52028b05693b5efcf041ecaf3f28aa1fda5276b9f46ee2298359be7df819c8f9716f98fcee097bd56031aa261511b8b45d58b3b57420762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af728b54f3e09285c01fe5c04de950b7bbf7f70001c27db5ac6b1ac5b00397116dd56875e4b468970631ca00a85f2ff183014402f55a87c05355553781089b44aa7bc906fb09035023b8c1049ef77ea11c505582c215e6241d15aa5747904b94fc7606c1c2f502a38b92f3e417df0ea3dd8a9b06d6ecae358569323106ef41d4519881a00ca5dae41f88bbd08de1ac87ab24cf8032e06d67c938a0b0cabcb24671552e337c9ef31265783cb67beed02730b67b95fdcfb2c6e8ca6b610ecb0106e1d71656082db334a0f140a1f44deb1744874582c2bf28d0633b6a44de83b4c20c52b90099474313f7e66b674c32dcf601b36a623466a436edb8f2a206d8bf151a2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507d9de053876d5b2928245d60542b38781c9e2c12f2181f7cde344d5d62c9924c67ceea36b790de753724ea27342f6142a949a02d67586e69ddad9374a9b3036f2c40b52afb467f4bc487e049553fd7567c7526429512b6274e32a65228c3d141018b457a2b77801d399e353a1a59861cb108063e8f70791a5fd9ef6b3bc0851d96a52a6a5043f7520021d87b16ab29756d518b780781ef1c2d2ebe2d65f7c7057df3696255ff31543f70b759f3a0c248cd40266fcb9d227d667dda48a7323f02af34ba64dfe9815a3fa142465c4a5062810d31402d1096347407ad15904047153090493449c2e66d65ca3155e4479b29d2bd046203c4af000c90961348c3e06b6b8576259c84fc3d8247a46fdae6673791068717a945f84d19422841358fb63cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503d5f4f483be55546c7c14c2ac7d87c38afd25a6aba50d60b4cf2413dc527fd3b1d35ff2a7b629e790115960e52447f137a7852383b25a00c2674dd2617748a5b713d703368a268721e7de82deb194c7cb9efea3b68b1d715b890c32b5b41240086b3422a27ff7b7bf47a1a1edc02534092118f0d5e6b9c17c6d1ac40119e986025276063e641dd118a5abb6932db3b10a0ccd664db6908020d6d1778a644d011dee31329e7c3d4660888e300295011567ce1722b93e1fb6fb4c3e46b5cf31936ff4bb45f4f5e4b1d91c21866cd425d34a84ef05c660cae37c488511d9e7bda4d5e78390cf6120d77cf06361387e4eb6c46fa042f57c0db1608abbb59a9218103bd193d56181be37d5f06b87582168d7dbbba590970f9dc0097eb8c19889f3d12f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aaa2c445871f62100a553611743fc35798e3a233e989b451eb928c52ff060e0162d9fc70feb053553ea6a111f2bd8246bafc80801cb1ae8132dc3ba2c4f819d0f1845845a95164e133f2d3b54475d8d687951e821f108761925666c75212aca54d2731a350aab520c5b4d28179f86916080d9971d757d391944183738e1b0ba7d5200766952715a7621d1c36a0a83672477203e148ef0b5658664ce64c80a8012fd85144f0fb876713c298608d2177602910f932e1bb8a8668cd66e12186ff62c63e630794989b965d7ede64004df19049fe6ef6af63f213f028fa058160aa53576a6333f4297002b7bc9cf679da4221f0b52bc1c670a60411995e66254a4e02b429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a52fdbf2bb778ab3eac9fd4569cca2a6504c147757fd40409ebcaf03400f53b360478a05eb4abb1306d32d92f5ae97f6eacd4cc76859f396cceba2a775928457adb6a915b659dd80d0a6fde1001d22116f80dd6769b1a5909b308b8196181a42b995159048acaef62b08b154afe1e201ed778d645e680972862652761f9ecf41e5e6d17085909ac63bbdc275d0bb56f00a7ef794ed9fde54d77988a0120f25055b23fa52c7cccbd1271731248d127a16838b3872b4c54660c8b852d456507b7579d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a506e54757dcefac37303be453f3684191b5ae6a64e7e27af28871173692970ac2d5324b95d42f1616ed9b39e24873aee78eae41d39360af13041319b09023f0d2a2b3c4158570014272b373577d1137b577e8e536ed4e53a5278c7392b48b975783be07c35d03513797fbcdc180a30461e7ed4453abcbe8f3533e0d90401b25b6c72fd8726f4ab365faeaf456743561530649dbe5ed4b9ff542c919b3b7ba67949b5f487670202fe5d0e31c07a5dd8586fe4fcda5c01b48d3ee7f6014b1a1c246a1e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505300cf23483b2c0dcbbcca21e91cd73beb396e1639150d18adb5c375f26f266bf66a2c269eb17434454a9310df14ea5a95047f15c64e7c6e27cd701a328b0c305fe2d226b264b77a207acd12cbd7ad4b291ebe609db54e1cae50ed5dc1eccf6fefcd85238584820424d00e5750bf1b330fcf797dbce9ad138c8b5947dcdd7e6647c8541b47ba164e716efb2dff4cf13cf7f5f565efc614728b633f1d64a993755f5c3d71a8cd506e89940c02c20a5c3b1bc8b63c44efe939b346950c44e9590b4558f778373f140b65f4af150e641f051a6c393c2a21b27d05e1ac029f152a6b11e9c87ee010312eb5274e42d214e50dafb45771286333604878897846fb57555a22a249c29ba54223c6654b3937ff714b4bec0b97e8565cf3982a259807fd4430efe742f8a3b60988baf1426b56dd6151167a04d9c5e65b99e085776893765f16aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a36c1b43cfe64e732b7ba0431e40c406d373df72ebda8144db2e2ea2b87f67561c9a4760c1e0e3347a33bd22e018fdf26aaaa6508f568ee4341a4de2716e5ad4f9b887d5c3e6f79026a1df30c13018c1dc3be11259280c02f92bd8b1579a4950e327ccd7ea2d0643e6ad0695cd5c203459ef20477e723e72c361f4665517d623556053460b2f1717164b2832eb7b9ee59d282c473fb35f455648640696f68d92f73737866388de76adce73315be4b81799bcd13196f0f34054eeee016f7fdef2c5b7e876f5efe87156c63bb55437307079f22b221486b4b629b3a6e517c58db39fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50432cff3c6d98d9593a6c693159b3de3611c923004a5cc91984d2156b40c6bb72a38e5229cfd8ec574a9ecd4eb7413a746b19d5141b9e6744b677403a26b5852808bf0839fcc25b708b8bb62a0623476c0f7eaf6971548111841fc35945b6710c477be861a06b6874a3b0d85ef3489313cc161d31762b7310194a202a5cca972a1d140754bac9124e0610c97c51365626f8d0960792c2530aa80e8f089fec7e1d70b6b9318e73562529e06518973dc955a4e01442238f3c148c5bd16ac9ece2649193600cbd13f36068b48416a972de4d81850d4266ffee6f515eee731ddace0587b4ad0bda761c738ff06f54e52e320e7bb89c44f7e6784bc1593936c8fb997be26c077d10d728320cf9c7797155a87b1ae8362162f6477d1e89ba1605ef83426966e9481f9e6f50f60cef04cec6c218c563630750c1067deecf1110f37dcd5db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a445d5b01b1e3435fae713348bf8bbc70285a165336a8bf6155bcc533b9dde913fddde82a34d3fa1783ac474b6203a9706f3bdc2f99f96e1c60d55d7ce0de4c398406e350167f175437724a4e3956d7572af3d662f210722f177a2f71d56ec9017e941f2ab509b14b1cb1c44104a52f477386645ebfec8024d0c5d30835c2787d40a0c3212276836635d8e524b9ea3322cdc3562c55408c110566585e25de7462ccfb7e5eea1d77581e2a9161f1603f42f4886c757caff573c1f798272750c6497462df04109c2d4c5bb40b125e070e7cc3d4636c57d7f7003fd7012203b8511bc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503327b927785402420a87664066176155aa51376a56b0e2499936b6671930c3695865e85eb23bc36d14e2e7460e153a5fd225de051d524f74d1151858ba853c083cc1e919b537977224e8f509e6628568ad939805e49681620c20cf221fc55a12577d4c2ba487b0673c08276ab37ac75990e6f9435aa53576d09aeb1ea820c1185fb03b19372c31516cc14f4a0230207e60a63954b21c702ab4fc5867fae21963dc94d64ff0c0fc49ee0d2c7b390e985e122ff116cec1de6552e02a19a9f20b0492a6960f39247547c07dbd616c4a8c2bd725fc1405bf0324eb859d65fb6dda596e49d17b02ae50214416427a13ad7e23ea315200cb7f9406a555ae476beeb06e1c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507f0eac38403b600e87dbbd1fd327ed68e3b4742903164e4e5a0bd804debc404cb8c04812af725f1c12269f0c1fdb2d12e8b2be2a5ef4a019737b5a29934bf249887e4a6165b17a0723c36c15ad91037b7d1d3108329eeb0fa5918c531d3ca264eb6b1c6e28b24a6e6085c17bc25fd10db8945049fe6b1440da30a868ced6cc2a2046602ed18c973522445e4be102dc5e7ad9f342b991e53ebf5d6a665b9d5218511405412aa54e4fe54dac0707698c49878553099bb30408efcc5971a7e5b60b77ad90087d13bc730e6f42031589735fc45bc945878557001373d300908ae32ff4665043bc0b5e1bd5e6e13042ea4b6882eec62b90a8444b2b7974661227757d25d7b53df8f30074c2e4707612449b6c6e63d80cb68ebd3498526058d4b6a52f0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5088e886583be13b735921141ba3abdc43b4eb377e98ad9423bb98b400da63763d713f3f33af39067892af75793656845b78250402e2824373b5132a419477a6180976874a07f82863dd786b25889bd27a368bbd2fa0d4ae3a4905eb79c63048799807e06437aa6040a116d7355eea6e6aaa6d793306f6847b4b5b8b6d47224f53c2324442d66429125102f3763d07277258f70170caea084149aa3e2efd3f8c03667d0e5c78abd07d5307571622d25d548aac713011c24040c9f5ff5282a67078ba9f767c51689c454cef7e05d4cdeb1ec2b6b65297332f67e8dd385476d6ba2a7c8f7f44ce1abe32fc96eb5cffbc4006fd97303c15b01d507c21f63f18e82205cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e2712157052d35062e09af4fe4d47b14daadff0eacd220189c0f5d0753eb9462361c5242718cd109e68930289c78330310603f688e074e3097cdc726c8e05a4032fe4d3cfceb2c5d63e17f60fca37676b6236909fce3823826d1d40386ff8f21ebf5b67c49eff200c5795d7242a57429018b5d1bce21b2233c96781276eaf83723940b618637be771ecbd83b2cd07e20d6c8cd5f6720ef0fc25640193e93855873737866388de76adce73315be4b81799bcd13196f0f34054eeee016f7fdef2c5b7e876f5efe87156c63bb55437307079f22b221486b4b629b3a6e517c58db39fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50eff5f73043680d66a7c8f20e45d10b23cde43d0c53b74d4f5bf3ed775c09b60a66ec052bc34e586c699c57622c42c647a606b464bf06ad7174795d4292dcdc4c4dad840280199f051bbe8d78d98b432001344800d1436a0edc132f54004a6509897d1e2ada1471727398c11e83773b47c1451d4d5d80ff551fd8952e4df16370bf8eb5090b24941ffe83bf230036781d1ece4d60e6094955f65cb165eec50e629208297a6321b003fca5950f3405c1159f56bc23c2da4d51c36255260cdbc927aeff9d3bb5e2eb171fe2df26c5c4aa09bb6824707a8648479e5cba7689792a21987a0301e14def511b9fe01f965d5c36dafca71768fa74097e49e84248abf9133110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acae7271fe0f437651267481dad9f4645636e664b82bdfb3cd5bf385e29f6c82e092ceb6674af2a347c2bd119143b957eb483853a76bd7756f8188a45a1e88a602d585c5dd4f33313bf82f7330cce90462bdfce446748071b55c59c54efe107147a23420aacd7b22abb185135cc0e6d4a3abda0451eb33d4f5108b23eeb77806aaba1d277e40ef24471ccc5671508cc3acc4e4756ddfb6b27af7a563f38804166c447756f6098253a1ec7dc55e4e7c969a6cc2e3b399e751ca6f3c778bac7ef2e1f6bf42526054640950ae575e7adeb6f7e874216ad01c54247c4f8559f0009694ffb5f48ab93674c31a2b90d1f2eea431fae770370c2df7d2aaf841e373f7b1dec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acb8c3c4260b19a066c453452a9caae7d82112617bfe9db28a7e7e96fe9f87c4eed730f2ef7aa623a952157425c0b335658c68370a2b9851e7139d74ec0c214390e44e334247c3e3f396b355614ef5253e7b64b6ff0d6080ed2dc403dbab67b5fd8562d3f6e4b863bcea67c755416c326bd55f03068112617eb643116a0461d5c7acf071a51b5242685ca8963e9584d2d500c4f0085b4772a0a49277d93751b4889ac836c71fe6d34f5dd293d98335c481a153e49be0c6e22ae5e34308e3f327845843a519b62f12273d0c30a4ef7da187341c010f21fe1008e20723a3a324650446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501895a92c569cf8127e5c1448e41854341c9a64299396c833518e8043be5ab310d1e34c30de9a1c3eda7ade05fbe39f52c91fe95bcfa4b60201f8c745cb5b34168e97591e3e6c3d251cb80973e8217d652abc51030be47a3c98cb066968c98a3e588ab854c708787b6c49a02565cd7e703f43f77631249c65fe337f21b00f572a5cf10978baccd3799522f15224ab667795664c348056fb3263d61e1d434464725af3023a02227803330436652154c7298783b33da94db25202e9470d773979098a765967aad2c2238b20f4400f897d4874a02d43d7330f5bb7b7400935f90646741c825f8069eb6e75b312384b42d13d78d81778a818e3330984983f3ac3d01c888b866efccada791f84996856da380b842376212cc0f65e6d936658e9b9bb78dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504896e440b6f508067d7760363640bb3443df881d21faff718ba55d5b4e7908304aa5c8664481825f8a201572d4513c1d9348b613b676365b352ba540a7beb3095688b641f3e51543a1ff730d28ae815e90ea296c07eb5245a4b1fb4b1a26bb7ed7c94e24508b8e119bcf5a3000affb7ac1c09f7870090321b31c34087a492c279d6f355830242d6980874d18bbbbe111df9c1d751aec3f4296cb837c8f1bcf0b618ac2275c165e11f60e3d510a921e35dcc0ff51560b7b1959e1076e7e21ad7e4ba22c7e65a95918b44fa021baa9453e0edff362cc335f5623a6d660abff77541cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504896e440b6f508067d7760363640bb3443df881d21faff718ba55d5b4e7908304aa5c8664481825f8a201572d4513c1d9348b613b676365b352ba540a7beb3095688b641f3e51543a1ff730d28ae815e90ea296c07eb5245a4b1fb4b1a26bb7ed7c94e24508b8e119bcf5a3000affb7ac1c09f7870090321b31c34087a492c279d6f355830242d6980874d18bbbbe111df9c1d751aec3f4296cb837c8f1bcf0b618ac2275c165e11f60e3d510a921e35dcc0ff51560b7b1959e1076e7e21ad7e4ba22c7e65a95918b44fa021baa9453e0edff362cc335f5623a6d660abff77541cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508adc2a14a4bedd46810add2604335946bd3074591709492c023fba2401373d0bdd0d02043c4448045175c36e7887395b019d0145e4f476069fe69b7b74f32057e141982c18bbfe40225dd56ae0846d7517b86d020402b5072355e761db72933d5842b729e58e412d99c2c30b66590f500b257e2712454f158e7b7c3e7e2d902ac8ea471a497bbe55078b4a647b5ab2355d89961c35d800016130e31cc149db7799c16b3287c18f0d09a66a6fee1d0409351cdd3582097a4b9eea6600bc34d90f6625281e0ac19324f822fa3a9b410c246953b55b636e741686d66a402b677a42f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ab751785c40296c1284592f33968c1d3f274e5d4795a177657b38c0455cc2e15345a36d46de535c41a7be14159ad8c90f1dbe3f610ef0050710e1014cd3501e23be3819104a742b233e3dd712cdc1e23b07ce982c3700fa2ff36ab7074a1d9a07a008373e1df3da6538dbce3356d75740545bf20c27e0e805d762434daf120261339c827996794b2c3ab40f7c900d471f976a36224487572604b1712ebec8e44987fb8b19f8db440ece4990090f5d7822dce4f01416cabf5949359a25aaaeb2596f89c531a1f3e734588c656ae3de354e76247a19dfb9dc4203a91a5855cb2748a30ddb5c08eaba75d946b1569579c3362aa7f12d35970816c6d76b6ab140047cb1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5cee9019e45278257d6bbe63e6137316a940810d408f141f90b5f76c9d59f64a90dd0c53b72996667cdc0a58d43c05396c9c53031bdfcd1692f7596753d9302d6128e50363e3c417994e6c7af07a7d0a4667a52a9e5075016d70ce51f61c1b653be07c35d03513797fbcdc180a30461e7ed4453abcbe8f3533e0d90401b25b6c72fd8726f4ab365faeaf456743561530649dbe5ed4b9ff542c919b3b7ba67949b5f487670202fe5d0e31c07a5dd8586fe4fcda5c01b48d3ee7f6014b1a1c246a1e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507f482e0306ed011cf3c8652ce9c4447da9249f431f773c628f043b5db6cdc031837ef8215dc9d54bf144f95d2c84652a835ea1751659962a065c410747b710593819a56eddd3503b0eabc37be1a4a00cbef4551f9983ef060a60264ab0f7de7ca39a940fcb23e12c9f3a2a5ffd969926a539c72517b3f33a28866b77be6c0568e9d10250f48a625ebd12505ee154ee58d544b05c800ee7782281f61cf91ad6366de0451b3bafdf501769fd4e2ff346665b9ce832dde7bc5c0c5234336d2d6164f3e17f326cdc793dd15a7b381da6fa5fc29a7d3e1455333ece942710fa4ea8714f82973f15a5c65e191c680bbd1a00237949457ccc1690461a84ac164fb4a42e82c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50504762008977b434f19c5f131856f46fbeb93f680829f14a87c25961cb84eb3e741e3105f098fd2de97bde28d8f7c477212a241e30f8d53df77b3c254a1ed976d0fea73337b66744e42e9f130cb6a77cafdd09406d715c657e1f63592b4c8a1d3fc3051caf7cb8358a1f1c74a34ac02fd0dccc05eb280a4e3ac69763981c0e1e115a1e11baa56b06d0eea34285819d523de8375b0b90e233a73a527a630c4a56b120c166d0c51646f2b6ca55b625a31b32ffec764e3832514d471e2762b4c52af2a7655350aaf55dd9fb542ea8672060cb700e6bedc9c64e397d1d18b989eb7ce18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5063dcd12c93f69c7ef9d5ac4094dd4f68926248359cd23d7cc8d651342d78bd0aa6f7f63d17ebc4200c75be4715f3ee09b4aac64ce1f6380d23500a195acb1811b3c4c330a6510424fa37ec267a53577df36dbf63b7e756422ab85f1e608bc2473378c12de6d1613511c7415a920b070e3aab3b581b9e906322a1031832c72f7a1dd6cc01af082817f763c1732c0fcf5fb9eb5b7e1c53a84b717bd95d8bfa1f35a22af2776955816fceb3296b33cbe05a4902bd1601c8b85fada64322089fce6c21846971431a11332a9a6605aace0d3e9a1bd244e1126c5da6a94551ee27d9082613d41f2797bd175834a8733c40de635258585c01923850c4f86d07cadafe32cc9034658f3b6f3f3d67e6692705a66307c8364c98b43364497bdc55fe85f02cc287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a0de90b195e55e32a7c55632a5c5bd31b1c9a7425bd05b5265dbdc16bf4e4b07dd041230f909389280b21aa6e100c88309e17b546c2f0a526631c2d378f4179472b93546fad436556a821b03170d6764a388d052e7013d803a579d302d76347424891c151ddfcdd69ba49a80546207d6c6fd78e3a8c284d61d7a72e444acb024f47f1b7567d2c9f24077d7e24bedd6b4250e6633e9be62d3dbfe2352cb6f97b0fbf1027180953dd791b5977712aceef40de90ad1e6860a5316a5ca11c1ecf1118e9fd874977316a4c0ccebe02aeb479682bb23030e523f516f2f18a40b416e543da00fe64fd0aee04013e583652f46c2fe4ca7261f343f95b6bd02143d46dcc07d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50bbedfc4d7bd98466642ddd2deff3f2514f1994508f45a5323e3e6417b365c306a442c91414006117d0bace4acd1dab5f5c4c29562bd2631e290a0572666863253c418164da8988393cfe5f3f6244815dd621110d5c514550df4d6d21de4d87236bbab447e7b77a0320957354c6515c308dcc1a3ead2ca12208d3ac6ed83f7e77fd50454ae00a1d151329de3ccc61f91009595d737f17b07c3cefd36c17b1534e71720a46f0dc84477d7bd33c59d2487af4c60767545e0a1931519d441c301212832aa842a50aca03932bb5005709a60395803c45dadf920f3ee758340803ff7afddffb26d99e136d2a2c592e07816112b69e8601a730323f68abfe435581686582c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e1f58c2414efb77b85e4fd69e7907b24d96516543c7c034b4a7133107084da28499a027db515ee496bb1ce00cae4716583f1d76cae942530facdc71cb17d9902b93399515146ce221e2bca62f3db7e187e51a801f492707b91edf34632995308c5867b566769e94467967d45b940c65cc6ee676017c01f489df4a22e1f3c712ae8e06d01967daa3d075d192655aace27971a5e3432e98f378fb30a622804cf31bc7c3054bd155857a6c94b560ab94875d3f71166d2438e75732f4b60d079dc1a1649526ae89dce0f3ecf8e323ea1673780904c04ec29004b69a9d47a5f010e6c4f67f045ce694b13d3f5a6264787314cf4f34d0b7a4b5c570e655d51fa47a76180edb530d3715b4a5e6b9e5dd071462d95ff7f3aa9b2351143ea5c7c452d0a0d7431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a2f1bc9229352da0543c47e661fa3e4486777931217f7cf330b7c895a137ddd2e84d5d41f0b343464afc62c28bbdc9b641231e578ca58303871efcf66c5ad2f365780de68fd5bad5d0a0dc640fb8e4e55d5c16e6f3f269718744d6c1d6540ff3e733553166f6d8d5996c8ed0069611a152cc4005bef13aa1a691cf802ae5d4e567e49fa05d2470b4e10d83d362a613e3a463fea36f5cdcc0b6cbe04189d1e3971e0663525f7ee3e5cd8469a56df3f187e0e52ee18f481ca7e3c19477017988e7863e630794989b965d7ede64004df19049fe6ef6af63f213f028fa058160aa53576a6333f4297002b7bc9cf679da4221f0b52bc1c670a60411995e66254a4e02b429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ac6a0612b6e26d47900941a48188c6645ae65cd561867f61c3c68d636bed5ab1d9a63644c482ea30b4b67d93c572bfd52a7b64632aa1e867e05ebe62513322f59698322609163da156441482e69cefb3e16a0c704a13d5c63fc928e5f53d51b60eea78a58d182f24fa8bc9b6f86eb180c7163023e6fb5dd31590ab6072da798488906ed3530b9f4744a83ad0dc1035b4c4721b47d896962550510ba156630b318d44dfd08ac0eed3d22898840427e5b0983725f20154b3e766c368843cb46205ee9fd874977316a4c0ccebe02aeb479682bb23030e523f516f2f18a40b416e543da00fe64fd0aee04013e583652f46c2fe4ca7261f343f95b6bd02143d46dcc07d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50166bf204921fa05df1c95752faaf5572c84f6376f931045c42a8dd1d35644659c9c1e424b2626513e9033a6347ccf064d10db925dfe1225dc46da3512b781d2c1fae59276d153357c7339843de3f8d0a214c0a11776e884a4ba00c3044ace336bf1fd606d092574391cefd604746fc46902e382dbe9a584eb335eb7ead8fc73748b6606656cf1d5c18458d3b7fa5f3441346f7233e975a113186b412a2b8e514fa68a62a7358b834afdf251adbfb1706457d3765f36a7934cdc0eb68e1fe0862c8087869b4a33e5c59929d0c8ae966442774e8236e588905ca40755e6c25ed0a25233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a506f1df1013abd7a466ef2b8510afefd458046c514e10005667e1c691da65d441caae8940e1bff4f2e3985db034de8381695f32b6e9c53be60ac008d638071b14be7999c3d1985025a81d0887dd79b3b57ba65e1007029f16d99d21615183b982756aeac3adaaf340144d66e4f48e87a1ad865e44d3c3829642b4d3b47754d8e3a8825da2efb3e0161edd7b672d3dcce1bd26a522004326725b9b4fc45a8144537549696377500da0e9e04d4295ff6095c6b692e3c66cbb81e475a854e9b5e1518aa309f10e701a37cda0b863b6e34da3bd0a73358d85ce512cb4c742941421a1ada2e296cfb8b9d60536e7a7e59503e3d26958a6f218bc40ab09fa20d84e1cc56ecbcd547e5707843eb90041edc2ff20f1e60cd42456eca3238d9572e942afa4af3fd880ac753fe5024b0646d24faff50cbb1dc49208ac46a7e8882179b083951fbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a04246e5c6cd9022813991d5b711a883623e5e7393db5cb7cfb0b294ce723f13303c70f48da7e4629e3754879ac98e93ebad3af21ee4db740232ac603b5213d339ec2586dec5d7a2795e69d370207cf3d5e54023bfa145e06062bcb383508ad640b81f7108e84fc39dbe82216a48fbf600248566e8f113d14add58963f2abab724c3e87682dd582239ecf2a68764fb456c4fd495402523e4141f5c32d8e2a7423e188480a80994a5c446206567c0778689161b80cccaef6547120125378449b142d57c86920d8dc6770d8864d0f206562a5d891700510d24f3c22ac6b7995766e5d43c505af8743798f07844cf846040e82d48c49fad9851ea298fb2149b6a441ecbcd547e5707843eb90041edc2ff20f1e60cd42456eca3238d9572e942afa4af3fd880ac753fe5024b0646d24faff50cbb1dc49208ac46a7e8882179b083951fbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a90f088505c733940bd25490f87a780717d12682bce986e08ff947251cbdf5831d6899d10bb1fb12266da2343ff178e5a6edb3a168a1b992d8a78671b5d140a799c60236e8e4d0206366c4f1ecde11e319133be4dcccbd8470f9c9416efc2f73c51584f44864da5511b8be27d8111cc5107e78932ec37ea1928243120513c567dfbeecf1b5374fc03905f195158bd52282731d83cd23ddc22a22dea43b1cd984497472862dbd9e919847e715a26dc441db1f09c2e67d8387beb01090ab361bb0c8c6996307e8fbc291d2cff08425eb6050f02d6240b06cc13f145963ac2cb43442905e270eb4234120141dc5fc32bca1884f0453951e1e2088b103c3023bbf1595f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e1d8533151c89015c9ed334cc4089342c0997e1d70552218598f3e7144545e7d4d63572627c405170569145606282425c688db22f89ac4538187395a5e4610162ed06d65dfded91e2f9a08790749bd07a0a5a90b56a3d8603e09d941ad5c2d580deac47d9e6d5102c26f8a57d22c2f017f8f4748a4743b05c4bc3b393496ab13582acc4f89b35601d681b5654360fd2713c8953f7f289c02b029910435d59457dd53e77072d0c552e46cfc055921d574d1c4092ac6a7f747f07fad6ee39f3b536e99202eacaaba1d0f8c4363fb1540312e0faf42781d513f70f29500ad14df0920f47231a1073d56b269a7475139c25a3f21e76c8f4926509fe6040260b7e773888b866efccada791f84996856da380b842376212cc0f65e6d936658e9b9bb78dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a500ce22f6ee28171139c0d5227b5958e763b48d20cee88a56f689830250a68846a597e8a304c1cf51f961cfe6d55e9566210ff0e43c4a64051e996f93035e8155b09fec27499e9c36efa2e8b5e85b313332026983eb58f633ba647453840a72f6b3d00b027527eb115607ed40f4d01da19b5f9ec424fffd927034a282d33f496575ec22a23f30ece6fbe83372581f470061c14383f714ba21749424a0ad186ea1e054e6856041e8914ef2e7f2efea8373dbb263f04675daa45e3d80265b7bd484ada1d1226e1113f7b9994ea42bf572355ecfa826248ce787be0f23c6a256e7e2f10aae743e243b7591bf738796bb5173aa651657161cdd747b0a4c15603531b285a22a249c29ba54223c6654b3937ff714b4bec0b97e8565cf3982a259807fd4430efe742f8a3b60988baf1426b56dd6151167a04d9c5e65b99e085776893765f16aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5b1cbb70ec556b20373c363f1bfd695e61ce66420771624794fea718f317a13990020b2f81610571393bac4e6e95d500fd984e6feb40d143e15f3b2dfc83c27da3f03d019d0b846b2f47db1e188e635afb71eb3c9040a9375f6fc66257b4056ea924f0759b5dcc0ae25a8e2699e11d2c347e170be1537018305c9a631ee676273898df2cc0a74c0038164e32ddc3a7418b960734e16bea3bd4a1465077b83f2fefab576c018eea636b00643576593667c6dbb52cb5de37336bf9110d05000a2591acc558aebe1357bac71c57b7d3c773ec5aa9524a09d2400642c414178d5554c52b90099474313f7e66b674c32dcf601b36a623466a436edb8f2a206d8bf151a2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503ccb4950c2ce2732a673023a05c1ba685dda7c6d2b109f75abec381d1f51e96bf0122829963a686d4b0aa960edaf9307aefcb019b732a63fafc085694dad281140bf0a532670c23b0478be57a25e6b52a48a3002b1d318693d2e662a7c249807d5b7565f495333693d070d79161d05176f19377c64c3de4c0951b2609ccb8d033f89412e47db390235688b778de39127f098dd74d1813241e0db4e1fef5dd03b104f1c4892d96c2cde1752051d0e871c8caf8b367b83ee746095b72367e9cc187e800e1fcc11351fc89d64760b11c768c4223030b08d5173ffd71b2fc8b1457dd4ec69727adc0659dc21814d5d027442abde8e3d1c71493a12a6ed013db2090e0d67463a8edc5557d155bf686bd0530f74dd682d7d9e5d0d3e4f1b2b21bc7b1f25530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e0d49f02edce060ee5259068b23bbe0a145b6521830fe9546da3b007a2ab84010b41253007afa94b2ec79b614621744640b986441c00645d7256d13c99f3d7373d26056cec87db7361cfc40c18ab6d1149be204d5422614511e9f555c66bfb015f39803a55767934e9cd807abc51107b0e4b33023fc72d04b1c8bb532acaed178a37e54f52b40e49bed343106a598c2c6a90da54dd525621249de26c70834c64bf7871318be210151f4093637517ce749da8ff356be0e7333ea9e746487dff6490391a0d366ad0316944f87ad527d019cbee04524166ae69f8e31639f78a10785caed3765775ae00763c3066acef456150afc03d83d97962bcfeff3710452a6ce1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a09ae79459184ea2b8fb28a6c671a1700f832ba5944172630a86758520ae0a65442e5cf14a1ea8172999050327a4e08462ba8e109af840d4dbc2db319d5eecf72a3c64e63f9651d03cdb1e43c6855f11a87847300aa59ee0136fdc85bafb5230682cb833d26d83b465170046cb89cda60a40ba101e1c2a67ab1b28e08d1e6930bb0fa8649416c4318aedbb04295835866bfb63b6b03a5fe02e587c70919976e1a494af83427a3955ca706655e71955c5b2bf5211e9dab113c32ecc46e4ebc1f7d9ca75a674de5ca4726c6690201503159daa4a22dc57fee5b17a6a8725b7e7761a3ba4f6a1f571c5efe685435eeef0b01de770451c7ef701407196c001a885744a1574e5337b0f10f1be95f1be7b50b2f1c03cd3dee2ca300a4b95c659557b3009dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50f52ca4263763994db049d801485dbc2bc94ba35a3fea0705899c1f34357dc46d157ce544b6c9e063f470003adbb0a52e8bbe29203b323a5d2480b616b6212565f6d65e1a8e9e593b55004e6f636eef74174e397d40b106063007694070c7e1598cff3f5f2860850091c5c702393d0c0037bfb91e995cb916785e821de954d845aacd914a2a32765719d2633aa39b936a23291e3b2730bd73d759ad55b7a3ed6a4dee052c4bbf656551527257c197d30a5ca64131bb67587214f5ac30fc4dd35e452af635f6cb1664237a07285874ae13e1abd15908757f4de62c3c6876ee832fbbdff93209b8df6092726237a70e645aac7c270b88da415f3db7b140b02cbb5ffa1d332f00290d42c677913bc0ffa22513c90040ce317638b9a21535e4dc82112aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a89b3a244320ac131d6760e33cbbfe0639523a8557a86d766acf4d9245845bf4b7930a7538ffc446827d3a03c30970a4a6ffa845e3a91c822064cf200aa37d40955d4d0476b11cd0b6ba0d77dbb892443c8456c207214cf68eb035c298bf5907a14e5d24e3934ce7a3c4ede24289ada7ad243f567868d90065689de04f25e4568f627d4371ba0486026defb75566c9b5278533b4c2829536e409ca2670ef91d69bab67413d4c16b3680ed32436732cf3a69c85007d0c67a6a6e90fe211f20b6499db3455e62fec958113f8e72faff63320e6dd2729effe03bf36f8332283f170f3acfea6f932d3b1fe2f8770528ba005894dff014187a3b4bb051786beaefe81429d33c6b3c6103444d1d5466490abe196908f3334b0ac01be888a73d01026b14d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50445d5b01b1e3435fae713348bf8bbc70285a165336a8bf6155bcc533b9dde913fddde82a34d3fa1783ac474b6203a9706f3bdc2f99f96e1c60d55d7ce0de4c398406e350167f175437724a4e3956d7572af3d662f210722f177a2f71d56ec9017e941f2ab509b14b1cb1c44104a52f477386645ebfec8024d0c5d30835c2787d40a0c3212276836635d8e524b9ea3322cdc3562c55408c110566585e25de7462ccfb7e5eea1d77581e2a9161f1603f42f4886c757caff573c1f798272750c6497462df04109c2d4c5bb40b125e070e7cc3d4636c57d7f7003fd7012203b8511bc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5042941279d9e54b29b5afa7261a47f71bc898805bf7ce751db2f4bb4328d3f675442d7773fccf313f42bae80d782155613cba1877c80a46138c885e7e3242b85148567a1bc329613d129dec5629e95a114c35953f72058056e9271001689827761f7d8e16745c3a395aca127280e3eb1238523e2f38a36b1df8c51b01c76a904b57f9254d83183a5d4edcbd4c32b0d700af627a232417d9433e9b2b7079937c4823e00f29deb9320dc06f2040fdc6c900baf91e12612b0e1c5cb9cb36d67247632f406670ee2f565919c36f27bef234768ad1b364da85d41e1d7beb64b4c378342905e270eb4234120141dc5fc32bca1884f0453951e1e2088b103c3023bbf1595f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503de90652e0edc56ae436f4295cd1a804d1cfa25b496237338a15b367cc00e1734d0d7e6bb299d56ee189b808de66a4477b58a8202787274dc6e9252989c90f744e4f6a4e6a1ddb5154720e42c9f76e018fe69a09bd82ce264ba1ba35552d6f3d3da6863af65f0860420ba42fcc375c0cadb4de697fcb30580c865e43c0c3b626ca59d136d18ca11c4b484816efba9d426a66b26adf76d47a33c07d4b260af20d721ed6632e586a5ce14483104fcd3e2ea7ef56463cf520761786aa063a466448ea6220126ff6057d68a71a1af9a8890bac53f23ab7a33b442f4f2b540e89bc0e28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50d0aa23157c68a453eaa0b94cc281e901ce0785573e758043c174a87c1ad98c04d0642a62f4c90434065fe13d99e50c5297ff9c2766d34c4dc3f1294ac558942cb82d960c5392b3761dff5f726779d32ecf2d675441c1db5860c954517aee060c756b3d3afa99f13d9af3c837b65af518db9015785b6e1c0d0765d744b7d0a15c64f4ed215b2f2708718b3351bfc92a277882d7735ec5fe1332f1de1b70ab190416b57839b4c7bd4afe4b7519e8ea05573116a65cdb102a4afa0a7b5e62b7fe4845843a519b62f12273d0c30a4ef7da187341c010f21fe1008e20723a3a324650446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505d4e486ef07bf07a87e75373f7116f6d189ca8607538743292f5db043dbc0276c9adcc6c97e0f43c7f7761534539f11b179c2767d89c85038534180805c35f1abe798c12a3336b12f0b1b5520ff091205f8b8642f0f3d86d67dc9e1965984b79ca611c247ee03765afb39f2c49bc83240dbc444d88104300a05aa7136df04e1c6aced511e9e6264d07d0ba30495ce3452e11ec7abd13cc3dc986a32d25d47c3671bbb26b4ede6e5ddb58f87696f6401b95584715dd302d4a64b4e670af6874056213e42bca182f3ed167b71affa4511659eff469c1cc0f62191aff6bf75bf743340a843391cc67266afa64201f4eb90d6ec1c407a8a60d4876944871bcf72137e1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3abd5961296eba822eaff199445f609736bb91a138cc16a97a6357fe641ba19b4e734d8466a612f94625f130540b862c6ae9b3f236f7698b6d66a9046de8651e35b575bf55e8874659cd770c546e193e0b64c230354130760a68de6134dfc89e113bf28448ff77d60f218baf5341ac6517d5f7907b09499f604d57647c139b981a3998dc59c01b0b111e6cd657adada81e81b0b03172b0752470f5b96f35719d270cbbc154382e320e560a931e4b3cef5653fda41bbb5fe87cb08f1f310ca6210e27b44122b97b836c0b6ea86343a0b85f7a871010f7b4387ee556755f371d90354c2b5323aa67eb1d560efa0fd1b37a5aa93fdf1297ce9f4d8d753b045ee77f0451f6b4392b1fa628cdf08f4631e7ed4ac01b690963369a43f1ea9c12ee3c2116dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5018c8e56335d803751f23ca1cde67dc00646d2c132bc8371e39126d6f35adea41fcf0bc364a1b637dea2c7100f7c59f50cfe8dd12de7c0e4b9f3b2a731df72f45ff36e169d21ffa60c770e45ea20c9710e341dd5e388dea6b4ee6db3c05053946837b2868647067152ee7985c82cbc4775e9471773179e6282e5cf95faa2616073ffaeb644f47e06a3ffe904173036b2d3bc72c55b7e4162078469a6e8b988a77aeb3d779bb91e31b95cc6e71e0768b2013d76170db5ea956ee809f3771506a0df2a8804e8104ae35f3ab431b56caca17895de927c7cd9c1fdf2a8d290c99814a9699331ccef5675078c4b96376d3e0244ca5725e7dcaa56fa72072242e4fbc25e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a8c728657f00af3c23f6621f9513471d3fe5b439053ff36dc56dc507f7c3d21fec536d609a4b5e2ca744ee00b99ec479914042690bd9742c983e85385e2e4317074568587f6b592b596e2b44d31cd9111b7d5d4d7fd7430839c5e813f672ee024dc2581805f2d03181b61e0b0a5da77093c3024441159a0dc97bc907ec053828ec85266754add34c35dd96510bd7d618582b1d57a180e125baf62a5bcacce34075eb5b780fb72f6f22b89f29416f8347ac1e4f6af764d3603aecf613a16d6c484df5d863bd60c92f2e43c40ec9508e1961773b36dfe8302bc6d29a1d8f7d81433e2d620f86d7e052cdab786b2cb80824afd2814c897e201333df954e53e2594cc81e300f234cef7c2cc4c257ba10eb28476e8b1c3f7c534c7b791c64b7f6d66bc974be19b4e7a805299c1628a9f7c762b4d70b3851e5ea6220751e5e7d33a72f70bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501b9d06487557172a9ab2ae1fcfde624720fb3044774c36025ec97b744a25796a34d8442e5b7e62198092c74cab72990e3070632a9ce71c5f5bdc9e04f9c69127af7c011220ea2a4a0dadbb432330c07e0b0661214ff44952277b883ac71ec80ec3297042e1e6ab4f68afe060be9ef23eeb041a16ccf901409cd7ff1824abf67e9790ef02c1e41969b9af150cda73a42fa092e466a1a74f24fdc5a96e32c4ec18058fb02a3acfd5210ef01645cd06727eef52112bd06dcf42824e1860dfb1fe017ead87218cf4d24413c90e4c1e31a63bd7a5375f0dd05e19b6ee9c397814af6287b4ad0bda761c738ff06f54e52e320e7bb89c44f7e6784bc1593936c8fb997be26c077d10d728320cf9c7797155a87b1ae8362162f6477d1e89ba1605ef83426966e9481f9e6f50f60cef04cec6c218c563630750c1067deecf1110f37dcd5db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ae1ea7c1c231a8b33be9353403455ba7233d85841ebfb561093502342a2133f24c9707e421fd8792be8a43867567a09192bce3a4303d73603756f022623848c5ce8d6367323ed4872f859ac2fcb9dd618295da56a5f52461abd86233217e9191dcdebaa0c161c3d264883fc115b2a0364b26b3a3e57080769cb777f78f4dd3f477c28b8244e8d9f5f05a9197379b4e162d59f44552d4a5962fa4ed6024f030f451c36c81177ad9b230143de6c49e23a304e03d831cb54927eeb22bc440c2bb21e710ebc1df330ea79aab0762ce01ac525accc5f08fa10ff03be2b74128b014f2a7d08f65787d9d6381126c207e43c5e22a2b86376d1fa096bae03f343e1ddb513b1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a0535175f7949c143e58b911af5b77e30015bb75e048e7944b25bbc27da62c2609b726e1d562fac29e57203494df95365d9f0336d7319a143ada461421f83e64a1098714d104f546da5ff0c0faf527a2db717cf067a4d9c0cb37acc4677f72f31b447b60e2571331736647538c0d6366601492e5dbbed2842df3159381f1d367cb0373e4db897c077f2e42835bec956410c38fe1c9af07401d5147e4d553aa37214e92a33cd9090454d41716c71811f5e71151e6641507f40c8ea911b4fdff948ddad784e048c9d211562f3620bd5331d3a8b495a017b1a2c4fd427504b999b719df6b12846417f2fac86c766b8dc8979c30c26017029d805040bab693af92961ec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5fa51e3b62997a3fbf18e7569b5cd4124fef1a09a6ccb57e598b31211fba121c1edda873f081655cc6c8df79de562e2265c28e27cd840c0a47d5120f8d942f0bbd004c77f2f45d76f5236a2e53c12f5b66c8b032d6f34722b898f27457f4ed34075c5c79d8259734a7d89018ab68aa1748cdd3692d63ad67db4c5b06709b6523d20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5052977f02d7489036c85ae5042ac12d5666b1030876f48125663c405a9bacf850e0015234093a4a5bd28b5b30e6388005cf160005bd65e03076456944c10f7b15fddd54481b12ef466f32390556cfef691a4fa3766e23d1627075ef317cadeb24538ead2f8c867f012e65d03243c3df2af8e0b315a5ecc37656909036bae48335aba5aa7dda79bf20281ae0004212161476a66a100edd60375582976b5705631d850994360f363a53dc3cd2051a32c1644017aa743e2800429fb0ae2cde51d04fed172e49672c7029ebcce907f48ec50ada6f4205d163b71fb2c0282475316a43b7007d5767222808c954ac70815e41758bb6f570a0b80452deb77336f94a4d342e56ac0b6fc9aa182a73036ab0beaf47d0d97f4a123cdc3a8738b86dd2e047427431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a8034392b9dc136094ba04c3c2613902d3e89214efebf8b1c3eacfe04f94dd41910eb1b643a37732731e4242144a53118c5eb572e2c5e2404250dbf1da54fcc0e37d6d978af54a872adc0e507d855652749430f49968b0f2be41e6c6f84aadc75d5e7f34288c3b773e3eed90776e77a074cfd2c5c845d562b097ce36b0594aa539962a70fd47b996d69a9876648ba7a6c44b4b43c5b608f3e48862074e4387e11fdf3804cb6670c073a108a41cca3d26306c7f01cf499b7617da98331d994d1796f4c9e0f71e40569886a755385074413ec03046dd25bcf394a851c3efc479f445e78390cf6120d77cf06361387e4eb6c46fa042f57c0db1608abbb59a9218103bd193d56181be37d5f06b87582168d7dbbba590970f9dc0097eb8c19889f3d12f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a525bb1168cdbd428b6a3034c1d157a1d7251e43a762ed037e71fd343dd82b14b128a230467482b2e6a12b16f5395722355c1b8417868ac5e28d0a3699bc6685b99d08a355b368e3ef04cd6663b7a595dfea9fc2afa3c1a4fe10b14647bcfcb5367e03a1649554466c6efce0a9b72e7493a388e54606eac0b4db09d75d3d4495d9b9a0433e0fb3a056d13f21713ae701da73b044246848075f056a66034c88c2536270278842bd008ad9dad78490af83be9318f0458c94e2c760ab13b46ec3846f0d7383a3da52f6ae646166ae828146c9d31993b0f667a074108fa15a2bfe80353f98c140d786117906f216077eafa482c44d04be32bb04ada38390205f46377115c643658d9df1267200a3fd051ab24eb92987837ea0a3dab6353707f9a094b70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50974f0260d332592e6fef9d4ba261bc102be365466a555b258cf3c425a282e748175afb247ccf2864d9f33e20f2c69d1778a4a42f66b8b353f61d985c1699386e7980f606e676046d0e147f18e2ef55081ff2a909316b5d059bd1a0192dca9b444a2df8208d75b43b5be4df5a9e32b97bf44c1e05ab75da31a7525564228f9655211fa7346c2b916a42d7073cf718070900d27722eb011419523ece57689e3c5062d2670df333222dd3a9da69067ed710246a813c90a00c04330b420bf3aaf255004f225cc787a206f8800521bf112444c32b160ab8903e3cfd1dcf486bb54b0733216865d0a5c37e4796bd317802da2d10f48f59a8cb010ea2b2781467237f0548111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504b505c7c875c80362b21cf267454fa5668c0785d7e6bdd3924267c59e5da3020914b757673914b73e54d392876b5f94967361d19bb628d5827501d713dd4fd5b1321e27d6fa6ed713e7c7b4d64641105b984652478a22349dc25385ac909540ce4d25f5d98e04c0997f65b4e96127d5f06b9ab120e895931e828d819c586cc7242e5aa65904e763993b59d6d241cac7037c23b6191216b20648c30663cb040118777276dce6b933d906aba4a8d0b74790e91f9351dfee214528768210283ff7c92a6960f39247547c07dbd616c4a8c2bd725fc1405bf0324eb859d65fb6dda596e49d17b02ae50214416427a13ad7e23ea315200cb7f9406a555ae476beeb06e1c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50c583a62a6664697866230b12d99ae40d8ab39631af319c722462e2364d5129015c900a2326568a38330e795585ebb0118edc27052535394adda8502dbb0bfd1cfe21684c2836a87b757ce31ea4ff751ba227893a745b69059e527b0bd3c2ee696915c26254a5912b9df47b4f4cbb85393ebb5e5be564355ada711d268890202a931b735aed3f7a517e6cce2ca7f7f64b9bfa8c2df68d20067832395ee3a3c173342de105f4cb356a9091a60181f85b31345f655e440e932e8cce2652e62aab6adc19484cfa408d0aa012fd17e68c0b1500932a47f8f74a0311cd5b6afa1e661d28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50637e88382e09f6523fa6fd64c4110611c435861ca32db87248e4264328c0fa627c6a622c260b84129df6fc12778920276b19a02c30918b48eeb44e19a0563545d73a7100104e9d1baabc947d69c49a3ed757ae46e9b9537e072a1272ed77d5790bbb1d20ed7308593748954073f1804fe16064210e405b0c5143e21a6d9e9b27aa410414c80537391a11e016ad13d73a4985383a7a28da6d7315fd0a3df45d699233787406c2cb2668e62c576db40e688fd280105c3a4c4b84a80b436ee9945758dba1104a7ab2581f8062751e3bfe5502850c75ab48db36b956ac09b854ca16f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aeda98b3f6a6a63477e7dd83b3b23ae2904945928f042a52dff2d822555822d4d1a64a07611470c26d630d33d4117ab2e75b08f7bbce9386e7cd2b04c23e7c858a2fb042825f0215af860a951ebe5970dd56d080705f4eb19a04b031b7d08e3481b401a5c23d75434ef61dd7d265d8731862c21519c5af077f8711f0ffbe1536014a71f2f9df5a11407c75b10d55fb4132aaab93574637347cc3d7c5569772a71b0bbe74e7354da64f903e815ce05e26f12648426d863eb30fc75d84de0cf8906112d3e4e9d022912876d6345253a1679c1f2515151952e470ad0bb4883d00d4e3bd5eb407486ad474dc257596bba07608da43f709fa11902e86d025544cea82c081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50dcd69a3025f78c79f76cce17d70c6f506f3ca01a4bf6110415828302ab2784513c471835f486002a27641d435cc5ff0cdbf1da1336e38751c33a1551d8c23430e771547b7be3cd243825dc4445db7a6c0818940cb2943b73f0eba55331081a0b12147307f012b5482eded515040688552b16c50f8f68d512264e0e7d13f01d2f71ab475cc0430f740918af45d997a57e1828b262b5f09f11837b5047848df5752c9aa057109f7d37d7f1a32dab135d5f1f41d01b3d972d526a7f364df825715413ac9045c62a811e5ff0c914280e3c5b088fbb048d5af76e6015995315a75c13510b2a4a3740363a8ee6490041d7fa08f40dfd096efee973e33d8b6ef8ca43575fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501d908d4d329ba95b6adc336cc5ef8332f5906d12e98b71142d6bb4573cef9f76dcfb025062e55673fcc7e1063d70a735ccf5b463d4e1a60391c4e42bf843cb5baf041455ffa23629e40c17489d6eb071f383835bbf54d77e7d0c514bd7f56810a298684bc6a7ce3a56849f17d4eaeb2aa5ad78245489e42e3e2a752f1a695909c512f30cc7821669241b6b007b1eb3355f0259784689a626df3e256bdb97003f14e92a33cd9090454d41716c71811f5e71151e6641507f40c8ea911b4fdff948ddad784e048c9d211562f3620bd5331d3a8b495a017b1a2c4fd427504b999b719df6b12846417f2fac86c766b8dc8979c30c26017029d805040bab693af92961ec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a38a899724af0041cb282091f8ce00702e10b0c244d48ce4c3e4b7b743c45f56194828b2eb475a52cb8c2fc27558ea414127e4037cdd69c5f41157a4998510215bce7863a72dc7614c0038b2c85631c6dd74b2e772f827333510f4b15965da16a1db5d6032735f05f96eff44b37ed11211d68d61fa5cbc02ca42ab24f0679f247d0d5550585b2ab51eeb9a66eff1eca05d7794b6c49921f4791d9db028dd76d245a22b51494116b10073f85731576a70635670638b06c971cf6956e025565d925c779e437d6cf907dca12117c57188e21bc202931be3f474c66cda87d04f816313c772e5fef3d3764846a6a4c478a3878ca6bfb1bc8da037154943542a8c1556563ed0361bcdfa906ea7a3b4ddaf17728cccd9c29f1226e4cfee6d56962ddc62321a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504cdfcb6ae7272d042e43a16086a4532c4963102214332f22c9624328c906954e6cdb330054c71508799ab130c2bd015302bbc84243c02b7256f39d736e062e157b7db77dced121766f748524732f9861de9cec32a66a2906d783d42a0e7ce639c13554635c00c4721b682e4f60bb2b5bc3b6c518d985f255606a0c1d55e381085faba94dbd875c158f86ca0b3a5a4b2a43127145285cad77eb4b4a32eed1c33e45d2786c35d48628f4c1fc4d30062a78f429485c037e064e23643b47c0b3bd28f4325022b655383e6399927168e27d5d934ca46b4be2ee3c047f521ee5197844d9d06c08f3882663863ffd7bd269a462eecad50a09770a1c36ca3e5355716e5ba2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508d23fa02e45f6316bc2dfc78fa86330fbd483b37fe12f7157150d71c72b0052dc477ee40a65c1a265a2f701972760b269b4a6526b358c2574f6f5972c169aa41caf1dd2544a28607d3af59220acce14f95c4fb00cdf3c05cc4180726394965158a2b207715f2ed4b14e08f4a094e001fb3e5280e1f44221fe514144f7db8d275354d1d7d2f3eac728826a01e641bb72e5e512f15eee0f106140c157cebefce45331237136934110568172b40599c95564b18032bf9a875694f93384b97e4d41f1536ce168780714e591d52028b05693b5efcf041ecaf3f28aa1fda5276b9f46ee2298359be7df819c8f9716f98fcee097bd56031aa261511b8b45d58b3b57420762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a47c21b1abb0a7e61dbd80e6a7ca6da1ec1f41d0a0cd3e7061c0dac210ab8766706508c2f9b9f060646bd63515c07ff7bf44bf90a59125039769ab15213350516736387280cc5c301a291d22576ee7b6b2caefd3a96d32b1bef8c6f672e058638bb1e42709adf1c471f36993d4e234d4da9c6324b6bd87d4a40d03c70db6b0f63b55a68528843854060afd54bcac3851badbbfc79042f9c46690d0b557cb15322f6b2b078da725720f3440a1a802f461e6532d72475962d4a25503f20f0f765639528150e5d293034558f0b6ea2908a634c487918d2ba9113dc31113d7c4f53499c5c9c2a6f308d0a553cab21713a6276aee8466042c543267461294e2978f720429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a099680065d8f6d2a1a72177994544503cc485a1e2e954452390e717e46be812667af1f0d49370141f08b3a441bc32a4bb6b34d39e7c0107af4a837540436dd3b21563a5149b64a13f2f113713d10a56f3a67db1357cb3948abad935f3f820c6f84da6f0c274c531a5212cf2cda84793462190d5114ccd825ac83e01fc5583b089cf9895a56e5710febb5aa0f455462481a2bdd39aab7c04f5018ee348790c06af1fa453f67092e7b2e5ec1072f24877a3b0ad369eec6b322442e0c062330c66bdda8ec7b8dc5a71e8ebd3e35e9ce2133586b6b32606c1075c079b818a30d4d651a825e72a87ddd2030962916a5599564f7565e0a5321d531b02dba61e938e949761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a560d2f196e14973b85d488120093be61246f122e4d79ad59803c62378c43d1213225604b45bebd1a916ab844a8f5e47a4c989e0edce4473cbf70cd45334b0c777095f731798c1f2d8dd5ef15d16f4b013faca04b877409052a0199114be0847ddf54f568ec31f9165fef6f726122173b8378f6070a2fbc587e588709211b223be97a1b44a3082626aa90c35e2691f165fe34e158d68b2708918e971d87c60b41494af83427a3955ca706655e71955c5b2bf5211e9dab113c32ecc46e4ebc1f7d9ca75a674de5ca4726c6690201503159daa4a22dc57fee5b17a6a8725b7e7761a3ba4f6a1f571c5efe685435eeef0b01de770451c7ef701407196c001a885744a1574e5337b0f10f1be95f1be7b50b2f1c03cd3dee2ca300a4b95c659557b3009dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5056659979a4569f419e6ad40a0e097263eb080f08a459a3280614da22c150f838833bdc51bb0b0f034ebcab2353089c3fff908411936e520e1b3dfe227d598550f0dd4059ba33284a10610341978a7e78e6e2316ba672d726f6caf637cda79012a48859338bb0a9151ab0421d222c1b423ed82d008f6ece3debb65805dfe2fa20a2fc883e645ebf5cc4954d2156add1237a89234d05599b7eae05f104ca14b422bfab330223382929588fdb075282876f906b562e92cf0f41c7449f4f786c790493befc2fc4a44736f4e91e301aef365977126c57b4084e506ef552274a91b03880c9a27c1a733655f2438a7d3b0fa138326c562f528980304ca13f45eb112b4fd2052b4440c87869f276874faae1037ab44d086b58451f6342334976500e9e70152d850c92ac5d2d4526305abc8764060d9f5049b98a192bbc5b2d76a294d03fab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a3d68d85c1056595aa77f1e783f624c0c4501674d15b7207ae450d6642124b44e3be41e56fcf6d624abe9ec58bbb507077049f47cac0f050dd8b153709fdd3a1d18b9872e9dfb734e56d4ea06d829b23bcd18a77d65bcac4312573a77f3f2404ca9520c2edb623913a077730ea5ef473dfdabf045cc82ed2c2fe0ce515a375e4ad20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503fd15f30733ccd6e1c214f5c2007ae4309cfcd7059939c67164b977e77f3b9562e58300826f2335a6eaa0528850dd657b3949a2faf2d4e186297e21bf86a96553bf96d5919e1af2a31c9eb5686d7ee2c792a3e026c61cc46120938111442c1487a3f0a67b4af7b4699b1341944f6095cc7f9c450eb39ea15965a2866279a6e523d219b4316585d5c1fc4be6472fcdf44122e8914b2efd652cd6b6f4c726e9f3e8022f61403a2f914296524584c1cd70ed8a1cf5be1631568bac7ae4ed8ce43104d2e821b072f7c4d2aa8e975f3329830b76b596551001a092fbdd05bbb43ab37446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50fdfdc2298f5b700abb0265567f9af1592ad0eb45b735a22c8d16a772ec23c9764c4db838c5ac90761f175f29ca4fff091f7bf247184d594c130c902065f95a0a402034647d4747354ee4322c21b740154f7a0262ea9b083c03980164dfe9db76a9520c2edb623913a077730ea5ef473dfdabf045cc82ed2c2fe0ce515a375e4ad20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504366144e7a778f5bd335d35797ef38131622eb794fd80b12487f923fcfa1bb5ac7d02f13fb97b873c303ab735535b3478b1f364fd5178d6bd160b40f4dbcee525d11746bcb62a37b471eb91496811d299dd6ec4da8e0775d3018a126bab9775cc40dc504e7706d634941fb09b5a3a9787bf19a0d0dcd071675f84e2eab6fe419b92abf0e00716866a8ccdd57c26fca2f0b09ab42c578134708b93b2dbefcac635f7a5b5d6bdcd4682c513933b8ae2438370a7f0dc0c4830bf8203c254e91cd2f3a3c4e025e2e39271a05d02c290acb5740abb204c617b033e7a2030d2d3398061a825e72a87ddd2030962916a5599564f7565e0a5321d531b02dba61e938e949761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa832bb1af226937c2b087452a1acbe73387aed4aaef3ee74c69a9c60759b370ed843ee3e4093f6326729ba74ed5684279b46f37772aa12780df504714898af6dbf3dab54dd5bbe19839c686d2e01c920c7c12123c362f55d2c10772e1cd2e241c7adcd2b3753725c960f5118b786c92c1bfdbf26ab5257541f4ef46094dd26696b972252233a404af73fbe6f106e3e1df324a47edcb0a409f4fe04047a94387e9d9f520c7bd08b7a101b642d740b40568afe9c0e8f25f45f11e97a4ab2bfe50077ad90087d13bc730e6f42031589735fc45bc945878557001373d300908ae32ff4665043bc0b5e1bd5e6e13042ea4b6882eec62b90a8444b2b7974661227757d25d7b53df8f30074c2e4707612449b6c6e63d80cb68ebd3498526058d4b6a52f0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a500a3d086e565ef01eed0e090b43330b12a1694b5d36a1d51f791f0720dea3f7104416f617b7d0b5545ac999603621426d7a14d552943d3b31a28bef58cd221465f854ab5761a3cd01c1b71c319063052232bea20391c10c23224a8d21a5c3b2731521f8027fd5030b434a045b72f430316cdefe3bf5dea922122a9d15c7b1fd499ed8db4012a53a699feebd6342f2bd351550d24df8de5a31bf353b59adc7c21421076b66e786e3578084a9396120864f0dc06266cca389181ecbd7082f24b209c47b6f6a5457f84c418e251fe41dd104c4192e11db444c6c4285c937fdc3476325233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b38ee016e519251c8fa9255972cd1c6786b886074e788044a2e8680d6db1a2476c04307b2e8dac78db721c15962a8f57a707e900aa1fd65d44880772b397975038c8387d929d8a57511a5c17173112073887c32c7926b42edad09e72c265da3d85c691443ea7b63f2a75f93edb63164f55803c7071d620097ead451c24674b4ab31dab1fa7793c1c594aca486d5d8b4522644c1437798f594cc71309cc37c802555a2d3919af75412377b13dad56c05427712839311e8b31d85d9c15927e67222e2f691c4bc21d4f9cfb064cd56a006f13fa3d65a29d7f05c097e353d78cda3ecc45bd736cbf2c4f62829c4fa6328f31e4dd9745afdad92685e3fd075b1768576b8576259c84fc3d8247a46fdae6673791068717a945f84d19422841358fb63cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a80cc643b6fd8c3d4defba2b9d932960329cbe38ee37622b58471c5f01a5cb722f571f4cae17e34a620c5902d0112b240fd1d52063e316228604fc280b2f011c1aed6c1333c90660da1b7c079713773737e86f167290df5bf9b1e91cda884075c2042172acfe1e6f7f6c0c230eaba7758cd3ce4b3d1521226ce4a00b61c0e00e65e43470104e781df6cc245f100f7653e9d36f4e200efb31aada07309a20852a56945c2eccfdad034e66c523ee4d9d7b7f9fc942b5bf2e14d3628c1ee1b4ba78ee845669eaa9762d3916a674c29b5f71609dea2f6ca9f5767c64e805c705d9433af4bc24c52faa360f8b710b79b2414710d861365c775629eed0cc7057dfd85efa6a10332dab380b73dfb10d96e9582b743d0c5759a1bd34d6f594513de6885f59e59220ebd09f67b6773627409c354c1be52654d9a7326252feff0d35cef93dbb169e2dbf02085cda3a3f48140214541f87cd48e8f7e839c404733531fdf44567498455004c842c6185381cbd4b9318b8bc430cd5b4571edf14135233d72a6708910e1cdd91fc03fd27e94e1f6c8e316ac63e11af9333255871e733d3819b52fb43d20c8c73fd78b064f6406178873eb1707a60ee13514953334011e9a79b02cdf4de438820574c4903aa49adccbe29c7079b3ec8406531167eea030cd64e1601adad2ccc21f735f88f4d074118e0659c9f8f295733734156bc4d3544096c155f23a37677d2c2449c53755e8490e050e8946d411d9dae3cc9fde579a8ab680ce83a0269d8b96c6cb237717715a5f259e5e9465915afec7bdba14360fa0dcd3d23d8cc564c2cef0dda143f381ceee973dff6535d40e45a4b1a6a14361757a5202d0cdf0742c392397980af166a855a7ea7a2aa26247e6f7df502133958b2b713ab113d78ea98e97dc70efd35eb5c36640be3dd71f925a60c8f82e6252bea6032232d4b054f66b4709b1a42563094e806c920ee556360af56c4706272e1e363733f95656c1fb83071e3a77f72302ebf698376987b25a7e80282c7b466c5f22840b60a9753f2d3af5de12787056be86a726d0f5e11fc50414180e4535f5a83341cac95037a3767f765c6a8d01425f9e802bc10cf24f6099f7b4035827d82d77374ec96c406745eb21d8938d03ffb62fa42002c8920fdf4652e180e34111c8a827c8d753c10d336024d11a8a1390a073e44b457600a4653d220f8e9d84a22926410570c097ba06c85069cd41a3f299c305cce103c59db80cf4e460e5d202662aa24d277c327321b7e2e75273c0671e43920a32b8e6bff6e654f7fd07322d70141168316936a567fb00ed316db7b0c2139014df6757632ec102635cef62b6c3ab213ece5aa107c7db25e3f605f12e240d53a42e8245b2c484f479e3aae7c3efc37474e82472b31c136361b41a0179e8cf360459928058f538c4f5bf3215fb997c9628a526a3c3093cf6eb5c44743e711426384e93a2800e41658756cb073cc96761dc8cfac09b1a65d2591f5a253b343627ef53ce549dda56e05ad6cef4b51ff5745a629b6705a148c2eafe6eb4cf221b4611f4b127c0dda90285c02e10fae759e32fdab5d5b8cecce0fde60453331fe3e6c3ebc434f09808c285c5c0f576324ce4270ad1a4edaccba0b881afb052215692847ef03565abccc0c0e220c0c0253c4604f3d093369804837eea6350a0efdb155f5e0f239372f413e5126d141bffac4612e39861282ddf6169e0c341352d3ef4617d51a1337dc252ff087516174b8af6e644e703e6eb0cc7259eb6660568fcb04c800626a46f88807b30bce48e7c9822f3f29124a93739d63ba587443a3770e0b4ae0016240d8b15446258c438d778044389c7b40ca5e7c52d4f2b575cf2b7e7829450e1de89895209835382018e23253b596342db91e3f508994aa224e20360122232c4f04db450aab97cb2af816977251a8f00197b80b4245d51b17136f0c2e8b33cf4959414a4d5bcd3342820318631b20f42a10c54d4815a8ba2b5e4faa1dcfab5c0b7d32253ccd3d346ef1568a574379d0302a508d198842d4406f4e7d3adfd82423e1a21173ee39d92fcc785d55dc8a9c2c2430d521665fb86889bf8b74b54efc324949a614b688035fef819d0e50519d4ba52141339c312b3afaf0a53c47291f7191d9cc5df0ce28171d68b933aacb5869f528d953248901635a2704402642460a23cced72ebc6c70fc6e0cb525bc1651cebe248746b5b993dad79d94ec3c1a5528dcc7a721f401039f0c67b220ac66656f5262e5ecba5d72b9b5982102757bc057f41ad0d2173b92f29fd9c1d73afc73bdfdf15038357b7541392be4a376fc26161136c5ddb19bd16e39f5a68fc7a5971f3b4625ee5ed3157785a1e1f75ab004262440c61e707121303efad237051ad4669a258175b079e66a1b96858fbbe3b42ef25e46b2ff285698f9acc002546775d1203f02f5665d60ab11a2d529aa4f976ca3b7663d545596b74c27f14c076573a4b9ee801eccee44d8c59b1212de59e16a354702926686b2666470d24534364156240f1612faabd67d1aad32fd0e1827cf8d3c041b8279d154f1e975e737240071c910e07e03ce857423c424a902ce5376b151b083ea3e1247b771f334211f91c9fe3fd5d81e7de21807a1113af472775991f637badc69426daf00f3bfc2074295eb7812c9d20531a5c343e5fe2807c0670e8de0003e7d04fe188f357af3d6f129bee5b0e632e654977956a64b5a41a64cf3d0809a65aac65631b6e3b55204b565b9fab15cea56f7c03a12906a5702209b9a7bf1aef469e0786d950249ba82a37c11d3f3666f4f00aed4fc736fb253300bb0a84558d350172759c7d75ec380d1eeed868124fd33f4e9d0cd500278dd73a2814f5676d7efd000bcb9c76fc9c11509195601d5aa1d623086d7b440a08d23dc1bc6d60a0b19a1e8ce5eb47210f787b6db2017254e9e24a66db554f0a78412e61c38768bbb1b73a2762045fb8cb01009a030a7b5ff09519288ac8298a677244d6ea2857b4fed854ff8a8a5f7ed76c68e39674683c330a5df0ca0303223b2c680b540a604ee5251747c4976511ceab61242bd849e060f94f56b6980135bd7352895b8c25d272fc1f5ded182dbe246a1a4d38fe74370494551e9fc9404601e00d9ac9bb1c587b0a50e6e84d760242723df9aceb55779ad663471af86816f81c38f6f1dc3e23d4082e6ad7ac4b138c7d2753b5b412a5d8c134e48f8a186f504a5a840e7258bbbeb62901f147624f040c62e5c04a63f0ecf3695e61120ef688515091b3594c29d45836557241219f437e42ef2d23763d8be36c9acfbf59e8e76f4924b5763631c49d6586e699773a5da909570d642eb1ee427a582fb43816a5914dadc63b36c64c9248c9ff7f634d28e6039c050727038a231191ad1b0dff1cc970ab39af444013303a1c700014704908774377081afe8c786f24490f2ee3156207d9244448117b4e56d200572b8f87ab132fb12c39912e12655d6b4f540bd6944120802c4581a6ca36eedfff6672249c360c66b5484967e155b459ec45076efa256cf471313a4dec47ea3aa725d83de10ba57d463810847875810d4c3fbd10e71d4ad0f67733150e6b0918101ce21fcc2a206d5f440b9a3478ce0a913c73395337d7c4636f1ecc667d679eb942e9ba20322ee9211f991dd35bc7c3c9691a8f677949a47660d226fa0a4d19534457d107160058ca15d033ae6f06da5557fdc4fa7135c3fd3b9a586631d2c7e148ee3be374bf8fd76d365ded705759ff79d1022d663f239a321bc95315f4427e6ed1b2af164e784d0fe261ef13fdd46a596105126dd018fd556cc01c17d12c161c99819d2e70b34b6d45acd03aa558c92dbf6068225ff59c7571bf106e2c1afa520ead1165b7208f4bf2b136510fce9c46fd39627a23db78746f648145dbffec720d57495bec2f5e202c4eff5af8ef980a565ecb400d736b6f29af5a61eae2b8199cebf36737feb55c9007fe147eb22c5ce2b9e812fbd033590476d9121fc4c61eebd755193d38803bc2020c105262e869acd654789ffc7207e8c8084cf004244f43d49e701b8e974e98af441353b9ba0217d4b526b3896a6dd9bdd20e587a1d13a1bcd17250c2952a5a5f620ad29ccd2cb1404957357e4d27f201764f74c0bb793b8cd81105f4234df596266b40af510cd62888257ab56773a8dd151de1e7256df2e1c60f2246785c0bc8eb3541626e699e97901032685b00c84d986ada31bc65c1d8ec5bc807df05620cfc6cefd78369af5cf85fd5fa651e7aa7452b4e460b5f2ef4df445b98dc16258c832b89cd574401208923813e304a4c78407d2a30353e4bbe9273d1ad54634406c648fa1dae6b1f484d6596ef1635153c7d2b5b8694115d3301107fb7b945ca8dc0609cc14a6bf00fef1000e68359b53fe033f485a91d7ca6113bcdcc7760ad550c351b17810224fb906e3bd7cc67b2e9be3b1b811342ea4bc1746189d705a62486710585a74e1afb052ac0818e0520d7a4404b52de1dad305314ea2b210ff305e812516cdd51c77e7d32cb292d2fc9a28473db84d325476278688820081e9f17820256540928025d420e0d89341f32ceea0b8f8f9d0c62e26772ca096d259af6170386d749013b4295262f13745758b6997aa1f03036707f210cad23e82ba4e8cf236d578b634f486a71b7f376755b3d5370f4ee930a19cf0d6d3bcd64646d8cba540f61a1741b7b566d6d93ca33414cca3d7f853f3896ee4357f4ef276b64a2fd2343441a0e40b00b7500dcf6427995432e1eeac8507d094232aa90540890f219543a69fa5236530d2e4c4ed37378bbc56e7bae025b3214e26bf199711483790a4079596d1b423b190503919179e7064631c33eb87e9d891f2b1f04ce7b00fe736d01744570b7600f56d795f73225f1cb4d1843f54f8793d878a800206c41a2d05a435de44746399d69bfe87c6366e06a67460d057dbecb431c7b61ea27100b9144c072a34d52b34e6e76e0eb0c1893c14cfdeeb7667709a15e16a70c2ddcdccf242aba5b1229c0b34f67a3d57ca5808f73ebb8603b35ce3753e49ae628cf5a535e81b8a92668d4a872cd12c703a3fc1f4975435d26c2548d5ee80cc52a1f0b74679e9f12464e82fd3fa4055e4fa2d92f53e196ea610e55b2399ca2c142fbf60f28849d3a3704a304056cb62b4ad8444c02fd139a6a463a236a07ef1a02252f6b2e4b49ea1913f2f4205fcc920c49a54711c0805d2d0f784232916f087c6871014dafc6770ef2018f288a91ba6089675873a4b60e6c86ebbd5ceada645149c5b0277333975a0133707ee41774555256cf405ff3600349ccad75cd1fe91ad46e0f0d256f066616d2112a8dcf1d5d965e7f2532c88773ff94491d34cec30d7af9aa5baa609606947b7a33ff235d48b4ce080b2b5dbc61ced40045f4fada5d4cb3357147c25f231b92886e2e82484793a1df5455b8aa050d017a66d6ee512910959f11181b8b5afda69776e5243740bd60d773649bc70ec30ebe2084fd00105e88b9075f4e7145544ca41eed2a0d7515230f048888621418f719493fd55666c379fe046f2322290c9d3a0b9a8fa74ef286c13b94a2ff4e4b21d8329ddd3402ca855e07ee834a2afb2b2577f2659f266bedf81a6c829b3ed4131f33e4e9311320d8366ebd08a8274085680c2c9004225c6b11346794c3516b4bbe67fd52890d903ae76c7f1d1f087419a57e4222f018dedadc3659363e7d1b980633cb907a75baecbb1eb1e1dc4144d4b6139ea7746b707b0d08052c722972304805a009cb6dfe29d4391d1a78130143f44363a70a1210cf872191fc94542076e0303ec1ec59db87935aa455e476ec831f62ed4778095339cc2c1e4f436e8d14172ca5ec1a5f99c40800281db86fce809f160ca34a13fa166507f5b28b1549b56f10a2bc980b912a903d0e900d747dc7e3258d5a3f399361f10405929a1da7453866ffeb24346a172f724292f216691c12533829c30a4fd2b17038fa046cbfa81818ab703c46dbb46632c8cb164b4265bb2f37c4d75d45176e3d80271e4962f5fa7e68ee8050bc443f0c86893206eb51f2722378e973f0413200c85c260d0b2a0f14ed35e92b2ce93306bdec55445fb36f0919652a5f17fb1854d1336d45e2ff0c470bbc1366b658116cc9291a79b645e82e5ab48239e3f7883b04d8b23fe4da0e07760f373c0f509031a587f93ea8876f1f6e68a2681c58d12e0dab3b222ec50012dd9547114d0b313adc7e23741df50105d918fc10b2b5036856c3bc49140022768987f37df02de214a3fa8c5382947b3968974322b5155a30aa199f622ea10d26b0ed823b6f980b62cf8aaa626c7e641c4e0bb9412546c75b7710e407153e017484746f38dba5dd481351986b58192d1660592b37c8e8fa143268cb29b4653d551dd16a3690bd35044ab48730eb6b7773897c484cf3f8ad4e2b8535780e41323e37ae82768ad8d2491d67177769862e3f247e8c01ed7e093e34370d445a25b52f483028485b126176b9ac8f5a24df000417c2237dbc64e57e36012e3437633c1aadd47c29f5cefc122a0ea2060f9bf11c9097007b15b8eb022999c8121494165bdd41fe1da8c8e77e855c4b429bab185b867ea275499d425bedd82e55f34200751736904abd4ee2012b90d74f397ce8598665c01d82e1f163c61fc97ba54b9f171df991321ab30352b258a9793113115ca822736417c4346563c5f3697c770878aa139827f9514b5e251446143585b3564f01f64b42af7f13ecd554404970254ea2ea9f4908bc9c36cf6c801a1d787e1ea654a57e0faa256a2291556b53b66644abae3e4f3291440d5479892638024e6ac5a4993066f82f2f67c34805612e5622686efc15d1bad2731a70e770856df3097381b868d66f61571a422807e384835b45d5bd4fbd9bb75d41e8cb0f7eb6fe7adb931d3a5818ba28cd547f1f1b96a758d4dfe12ea7f07f2bb8ec5b0a37bded0f50b2332f86d4f220ef28de453d904220890185733f5390285c54e042cdcca718b70c304ccb2dfa0b4efb9877fc917d39eb3c3b517dd7525cdafafe785d8c79299ba80f37280520296fd46067af08ca249f428b0381cbaa61b47f8f0c9072684f63155132f1fcdb17d2bfe75eb79b1c7ae47d644922ba895354e96e10946e922ae0d89b729ae933122bbb45778104a31d54f022509abec43b0ab2a225c2f8b26a28dfd520fcc8a405af1baa53235d3a6b619e6242c0180a75ee6e65490bb04358cbea0d1e5b3ab53208cfab0588783170230af03c1d2d505cccf8ba149216536866a1c71aedd2c209582a244b30477a70ba09992d0e2e64011fff4a6529c33e10fb15b53f1429a65978336c5e10de35402811360b88a18f4d9aa03d3912968624c91c54474a0ca643d944561abac9205e115fe76fcb670c394494cb56dd22354428a782321ded1b1f92c1d70b15d20b0cd85d327b4b6e02790bcda926ab2d6b30f008bc19fa7be264297eff1a65c18d4d753ec3565fdaf53e2f813d57eb9b331ccdede81a24fd2a313e4e515df424b456c2754a0746f18f2211042f5a0e0cb36a4978296338a0384c62c1012148fe1179c404692ad2651a24f8e7bd158c567b1031e9fa6329ecab1cd4e96826e553625c9068b82dae36933bb20d1210e8e39247ebae2c68fac130435890745bb10485105c2ef76b669a3466aceda3654352bf74c857037e7cb10572eec379173925d8367873f8424128c52104724b30f2513f2b11f9a57adb34877dddaa175107ee3f65128c7e1d122ead0d83a1f518d6548c58eee60e6ea794cc5528cf8252c0e60f649ea10f3c64085d56b56a7313bede4f54e75ec53d136b297e87c89a17de8fef1028edf962d6c17750eb23472f8362a763fa92af3025466650c016520c71f2c24350d31a106bfbfa58e1591b2a1ddae24080c803478ec0ef4bb22b2e2a67238857a0c28f631b78bb0eb0175c310ec7dc71f0717902c4decc4a0baeaf2d4585a748d20b2b61a61fa404e0de584f4891d734f514ac05f984c02a87bade3da03e886d3299dd3fd98d1c602959285dfafb3b6995f5be78bb25065aa7e3be314a47bc1324d6091cec2cbe548eaee2649db3e545320d461e16597b65d137ff4c4e45027d47601c3c07748c3616454859c6fe873f2a9ebf3cb9ebf3003002f50e248f930d78b0db200a53a04b2bdfb731772d1233dbe8ad7261fdae4de6950a725d27ed38a166552c63c192582df79a7958ec9063e053a04414fe0e761be2da4a105ec03b0ca0bf458cc84d6da1cf2d603dfea232bdac3600cf87b804c8f1fe54fec0ec09acbbed634d8e6356a3e7485c424cf8149793ea4d47e38725fd21c07910fdbf74323a2e4f0e9ffa29d42c5540d9b5f80db624185e3800e9652caf3f6bb76f7d3ae345922772b6a4031b9a33359e438d12dbf11847cd9ca81620247d4c9a565020e4adcc213f7b7809c0eadd4a5119435aa525b65a0bb0fd62c960e17efb9a2402c2d19177acb5c4439ee9f63470ae771595f5c049f1ba5a1fca18ab17e84dea52d305fd1f693bf200bdb4e90f2edaf473df55c56f5ec09c704d8bb952c5852504632b7837e81427491338852d2ee98e169648a70d7e5c9c1a9bd2270a40526458b72983360dbd2378e2945867884f524fac3dae39d3d79e5436441a6d5424cf2dd613dc1f12f6975bf323e34971bb0d33a7def93d9c928174a4ed0917a2b4f82d0e99042eb403cb41061470098192f92ebdb0bf1880cd9f3b72b5467dba27987c64bf7a6f3caab6208c0ff95b8d08aa3513a1925478f5e5019bd5287c409dcc2746529a39211ec92ce0fe243bc1d21a406f63d8468a5c9a603460bc57ffb0cb578f66057da26a300a4baffe0426269422c42227056bd8db01c629a00c6491575c64814f455d8cc829ead2fb314445ca521b4ad71311d8671cc7037665515b061fee230627f06bb072f55c97121636fa4bdc4543334783164383ecf52b99fb726b0c93cb20724cb374edc7cf2a1180863934a97e1b0be0fc7b8df7076ad0885c56a0cd8b515d9208730501fb3ebb315b00775fde1849c94378e54c21732ded0e5e7940987904ac9e1b9edc387e03e07c60413a3c00054f8d648967e051f287185455c62e1d2f0e4b1419f8811f462d03252411796fa090f96d3f15bf79c5041d20fa3dc919ad9210783adcf61eff08c6302dee90012a5b015b5fc3720abef0e1142a88675e76a02c14d6a1db603532a9163c07be779d2e221614c8a65fea45a418cd377c5899511242e7d8f973e5036569cc4be476f53e4236fa82535af6fd0c1b5dc5f0176183144e04e2b0322902335cc9165520c37f101332a77b15bf89e37c27d37007abe05b5dbaaddb789c5b3707fd26db5189777368792fdb1654dc94790e4855289043272dc63182389d6ed92944d0ee7161c17b0f7981645e9c3770267ff2825cecf9540819b5d67849d414307f4ca758df6ca125b20de8484fdbfc599e85d321a9f83563198f2a7a1dad4b324685a10ca288971fc003b56e0cc0cf35abf1e14bfd3a1364d9a32925137c5a17f757785956796b634cd73a4f13cccf6223ba4a69dee4d434e781553ac130b441b444034774f040776ce2ed3327705c13d146612ee39a8920b79e601b3dbeed16c3669b7828b9f351c6588977ccb1286db5025812baeb4c0cb1182065a4709914adad305a9d260d3b6471b23494b02e4aec79f14f08dcf933995a2866fdcd500019e6c15665faeb17eae54b6f8207a054c7abb54154516d6615f0ca5f3417774201ed044087dc8f756167786366f24750b4a376102365152d6672ba08bb6b3069750d394a5ce9563cec653f72da09aa451d7d74647ff0497c5d8f677da02c4b30e0f8453b344543040afe4f2e06cb46645e7e4651a4b4221292080d2f23e4747c741dfd0fea1933634a9c711172e152422b22be21651e25725f42781511bd9327864c481ee444b34c1b2da4136bd86a36bb75f3479614b410dd4eca5028aebc712d153f1d3b92bf06ea78493db3de1f3fc8384c35b29f641128d40a7aa71f9176e2cc730391cddb216766b72694cdd20d868db15b8d95cf1314d43c38dc6c8176bb62466db4e8e72557de5702b96c8306230bf3346a34a40289186a11f07d353f0e719f3fd858037070814e41facfbb4f54b3a219feb59e0fe0a030060d9e894246277c03a32f423d936d092562a6bc560bd6543822f7c544a35c6c78d8647b6a3858e9520b571d4aef748a1349da6c496f71c1677e453b5e238c5b3bdae56b7db277737b9c6b6f0529794725d05a77524e6dbe64f59fdb7c86c61f764654935718e3242a17dc5c085f17072e8dd07c1c75778a14d6a813734537d61e85e328631da4bf26d822d80b943b314534a54b506976ff196516bd092962cd16339c3b0a60199e311addf81f3cc5e937fcbf5b47b9ada17af8b6037b61a0ce60a2e910475b65df02fbc6b23533b63a01b9aeb335e700a56a0c7b50129b7637637858261aafb15e378a758c2bdab8c16e87a9ab4a19a89666fe00543accf89f5b95072d31f72bb46719d3372054be665d9da87e2e9b6a4b0765569463bfa3dc3600173c60f3980638cd5a8a069f2f0c71536eed02cacfdb451b8b4623bb7cac6283da9450be63c046fce2a467c12feb07d74ae2406084ed05d03aa15b329dd66d3ed8ad57732d4f5d75ca4b29736ec93e74c6a73be0377651865cfa3643c39b578666fa1f6f50404caf89fb2e6741006388a04d025476432ac51ddf3e6cf8a7191bb6746f7ddb8d1b798a1207ac55772b5b98415455539d326eb9cc7022b2fb3be8debb566adfae324eb1d26b414a4f485e0d4e6f99a9724f5486804d4fe1ec2e4cb7012c2d4f4f7792c8345bf7abcf1e11113e2c7ec0ef1f49186a2117d1b13d3f78616063d08a36e7ab2a6db77aa363fa2d9c4a4e5dd73d9b957d072fc4ec73b06db74c744d6a028b815b156cec0a3fd4f6231c07dbb55db3e88674df853e3df76ea94f2ec3e75315525a16a50d2d4c57f76300e646d53791c5ac0d92f9cc542f591064b9674a459f0094174c8ec548ec1c103e528cc86025d6b302cf3c643462ae750962877f0fca156851f64ccb357929a55b8d56d81e98e71d026dd4844349f2ca2a392d5630e89afe76f653e72587baf3614b289f281ccde10a502d6b12b2daf82d79661c70acbcfb4c47b1a21606a5491cac798b385bd93f4489ea1b3f9f04ce60a1cef60d5803bc42d44d542fb6a8132132489d16221f3c034784e36e7708864710bd4c5df630c57361c93843b73c673e905f6967fbfdb05c8942ab2dc38f4e2315dfe3598c968e654d582f183547897d9448c3335a42a6635b310d1b4a86783892a35c7e3aa09b2d85c0cb619c99066e1d4ad724ab0ebd34c24fe9211855973f45dbfb7a41f71e38f491a16fba2bf901dc280469c614b620ab8e7141a8c5313829b94a6f3811d909a4ab5a2392c8d04a1d532671926b937665e84554595c411f374cf91abbc547605be1e827f39ef3538feeae4ba2f9764a8062c555ae31a65e8ea74e41fdf96a1c8ac6332830702e11a04f392f674a26714202b26bbed5a940f6fd552ccf53fd4c6962eb08462f2f2a8bb1f5672ddb007e76bd015e5f3ef438755843741efc507e1c6094323d8eb432279ad30f23803b3026a7b105323979736c6d223f17c9f67e9a29ec0517e63d3a5fc86159bc126b74b60a335e01a444183e610b7dd969971d65a50d31ed644563d76a3f01e3c46d2c0fe18239f5d31f2482291c6c2ac6f466a093aa188cdad954d3856f7bf63d1c6a05075c652941900df791ed6d1be0791e41f0032d8d14cb2e47eec020f2b3767046492f15a1ae1a5ef9009f4943e3316aa1ac425795500e03cddac6558cfc214a7bee9344ac25445ee7686359f1486250df31930347635e152b290647dea62a14ef053e1bcebcf664562d655f3adaec57197590743206bd2823cddd1dcff605424a21472f9b3cf716bb4b6e63e309e95e2e8ef970b48b1e122f062f315a782d7d51dd9001805298614952383deb8b472a7e06d46d717519446562b76548c5f032e80fc75b20eaf65ca927b93fb9850a494307f073e558ea2143d9911db19bca21596a2079619e6946dca78e1534cc18045f854816e34c9632c1e8697a49b5e6253ad5ca079845181b74e60754895c835a9cc47c0d122bf5576a683f1e837d2449496c5a2146e61950d162140bf2baa847f5dad911e2e80a737499a401854a347d59d54e53d216f335e8dffe6a4136b03a7da0e40b5466ed742d47fb658bc99a5e6e570050c7427b296aa69a616b5dc013da355d75175d317a4c4b253759e58258190e032221324e0b75eea729aca83656cf8a34662391e55fafd5380256b4a564f9fcc06d60b5410cd2156a07f4c5114c4dc4b2754551a1263f9f6f13d154a17023b4685e98fdc84c604ca21f12a3870b402d235d79df3238a79eb92c0dcd5d32c19df121846c8778a95ae2094648f76a6ba650561ee454318808fc048886cc6289599240bd0fb7180b91fb09ff9d5d3e4979e333cab6171ac0d47b3f83bce933bf77e65862065004108d8f3f67534a0e5b232f26c439e47b602eb72fd734a22451fdae66b88431203cca887a04893e12d1b85d176218e86a0455137aadfdf0403826154ca92e890c157f2e66ccc442554b93637dabe67d1385c93651f3b7507196139818f03639730620f809d2f7844030963305b9d1e9777d792d630a9464738664d926f8379754462d8a00e501d17b7f41b36bb09c007904a5975d19b7ac06fc5f863b1e7fe67a231103583add2320d6c7373a869173039760557d43661a5c84a17d462ff6624982d6ad552054c91ec2f5da5010f68c24cc8822405725100907d82d302c0156798d3b0d62f31f5621c60a1b7a00932b0629a4154b047bf750e63f691a3519e50b246a3b2254d05b6e78e2a5479104b0633ed00b1cbae04b7e3e0309318b229106fd432665d26e2406c5ca8928e486d259abc89b0abb32580d4f4097782bdfa61f78fc7764e0dae87e17434700564fd439764b6929b28789473fea122b717b3f12c46e7a5932e819026234d47e16b10508cb1cc76511becf540f047d5ca1d9d657d39e1a78c2fdec2d172b71028308b43c3f17cc3ebef1540cb390471b8742590d866a93450c92ab48026dcd312dbe646d3b87f6028cbb5211cea88962bedf4d53c367316225bfc3247284f8367a4d434d7fef58779d467d1dc51fe14e867d4d4e8dea9045cc61227ac597794ed6ab2522445e7c730bb2f316b58d8471fb0b0e31de9aed044a0f367afe30b90c0e28bb170ecdac31faead3474635d40e2d10f37e7dd18543c72e997a910019454505ce79c64b0c03d9f86451b4dcbd02f2090e7bb135ba28e1bb796fee5d5f72b193313840479e4fde39406d257a0a48c86e092916c37235c917e07abb7979046a39533f4a0a651d13b36f3c6a60d3549fb5d9089195020bbf0a5f7ca9fc6b4e7ad2ff694ccaeb7de732d92bfbb7f6330afa7b0b9acece1a6a9a6c7081d45b75e63e4464d87e7b66ffd71b05f403cc3a4a30c82d1ba1ea28ffb5223d9329e564c018e465a4fc481e52be830eb5acc9427ffa5f194d0042197ed1a96080824d1efc88c33ee8d8ac617f11835d64b9722d76411521d60b4f273756cf04ffadf02ced110346aa8bf24846970b1fff31b6435082fa436847500f25d86c6dcddca82170d81059bd64a00839ac19626173b733fb99e7552385066aafb20f227d290571fae84931b6c59272a50f5b0d5eaf5b11afdef17442d54240d3b98c0edfa8396b6b096b6bd44b324dd3d4e27b324c110fed5cad0759d43133cc69385dd2dece444a696c1f1440525ecad9e9696c32617604ceec4a2e81f439f144d851ad91f42b01badc2124488b34eed59b611e206413f9b46a34de5f113638a31911ec229d1d49f13903daae804264760265f1458f15eba74052b9063823a449732869f0b04099bf78073af30358927c8f22e2c297527c33a9413af61020803223187ed2e866b92c436140be656eff93621fc0db8338a81d955aa305234ab8c98d2824bb4c583356520936922300876e990b0c2db46c53724d2de5789f6c80aa1c394d6bcd728d9aa62bceb8d028743bf7727eeb5059dced445f2ea37c0bfec6e24ae99f057e8ab7c321a12a0d5397b54412853925058f2a7439c4dd8b02400fd423edd0d80eb79dce292049887c3857ee6101e4c14ee61d01092550af769725fa1734615d6cd1e0d94f6bcc2362acffe667943f8f0e601f8620ef4f1b1a1e33d41bc271786985ddd47494bd7903536f6301dced5865737e5c62921f602d98e8754bfddb253529cc1001d8e00349644816493d3d3048cbf3424a367ac96fc501d40d6d759e10404f9d09c64852336bc11668af7608302b6a991572852e7934839019e089bf2112989f6bf75a5c6c7d9e28036c083d6236832246ab97ad4b3970cb1e46fd71528dc8074681f9c655fb5c7e0af2a6c4225ec16d36789c9f00a5e89372188b804416a5a146971643052d80d5669523de3a3350b069ce87ef1a7f62b2381ec2d97e22add8472401214717d1941a7ae72a129311f44d86434d5cd420fa432290b1728cc9e828f4a2684f7ad7d35ab5b0bd3d73428e686a5d996263a81423f7b7326c96a1f60ea188b01dd5cba424dd228f277ee81c5e1fc3fd11b4897d0fb9110d7c1ee54b5a7f450f18e577bc341c90bd10a28fbc1c34370e300804df563a463879a547a2709390bb1e8da2281f2a87bb66f0d5ae545ac00918cadef3469e45107beb6e745806dbdc30e3525d76054e7e47e5329845d255326aac1bd0445341da5b5ab57e34670a9e5557654352530e8f298267ba79cc19c422579e577476b4b3596e3e3e2b7c910d2cf6dd9246f7fff049affa516e1607aa1126403d627a6b387dd1bced1df1fabe1b2a500c7785b5e02044e5a42c6787c0649dd54f46e629bd1808ee1b79b71dbf364a0e0554e7470e2a7982fe145daa3763bd5e8203f8161364a6c7fe317f600367b082967808a0d268b51ac321339d2e4a5158155429afa8130fadb43727c65a0fc21add26d68c666ec2c7b52a47d85122d6527f0b1d39810669002a000db89503275e9320aa943f14e148d57cfe8e493ff2be531f5c5e420aed11043116e8536b4c22546746d9b63c0961d946f5b4db3c424e2e520f4aa55264b43c0eacbfd36fd066e80157671143f815cb2dba3ef9100ff4a14cca433f21b20408310283a2210da63d13549e9c4207720a366479e50f26eae13361af64151234af0e93e3cd60df2cbe4a472acf070e5edf0a6cffd96d35cc4b328d574b0af81cb777c24d2a6a8271777ed6a4be2d377b7e1d2602980e76077c3005cdfa410c78e4273ef0f909b1ec470e2b85ab0f8842652885dc576ecb3483614b7fb276a96e210c39e3e442099d135ad45559784a347e5cf143976c05b81916e205421a44b18d530318c608abb6ee7d9f3e96238a0b7a1df8ab0a02922e7c13f475cc35980a84466faa247e0abb141e4682b63ce505f82bf68d7365de360f2795169622d241e623ba77e43c1c480b518b82a15c7d7dfb696e66eb209068443a26b806292c3865283673716b5b8a45192a603418ee5b1259e214782fc916836aed9ccd52f56b990e2724965fdb86f221c378ca616e47684637143c4bbeeebb52a5e0e30a91bb3d6bbf98182591b22e57d0375d3003213c0cf4ce0d73a95ea95ca69007163805d66dbc9766203825367cd1f402702c7ba23bc396eb1911d14f70453ba10ced5b3b6ab8cd56539b0f166bd1d6622504663769dc6f12743b86c34bccd9185f8f7a2867615f6072bb465b3228bbae0e1739c518cfffa7622d9e277c8fde4a1fb28693724ff87d4a63fe5c0db4e4df4e6a682d2a8debd747a9538905db34615e90b4e90104b46c0023303d6381a12053e5e68f34931efb5202b9f96546e2707ee2590932e317d702fad3c263a6be8d5665ee754ad38c3b279870b7607be54375538c0276012af7606f13865a01a36a7c0daaeb684986f61fa5219c2f22733f472e59dc79cfe99e193e158937957b7743fe82fe4af9dbcf1e32033a226e29a10af3ad3c352f9fe905211e865cc933c0279503f8789b0b5b7d94328913609cdd6d66561325c9ae137186cb3f6373215e03cebcaa730ba2e86b23f4ad2e1907b9146127bf32e553857dde2e0d6d073a96624eaeab273d55c1690438c56061f97516f54cf63c028ccc03ded5f3021a484e0c6d44482c452b0004adb48909d4893a41102bec4b08cf847e394c2572dc2e58160dbf29603a8b3003e14e347c9e212f0fde5f2d15ae2d94256ba5ad1e906851747eb889620d39d41675b65608dfe0ff0245e37325ee54df7d1a41c20e566b676021433f6dd090b056636b11568870e92d6d335f4f012ef4467023b955bd59220bd8538c5b1fdcae090105df033e7b1d3bb4a664630e4b9d67ad283320e6a85a6ae21c1b640590a249111d2129d42a6a5245e3752bfddcbd559ce2785c69ea6670a04bf8393d85c47db267f508c3f2c7227894c46f48fac75029f4d923aa07f73677b5f51b0d49a15897a6f148d3133b1ebfad583083ef233e0cb8121f01a19933e491c9426d84125a9e0a29617ad1f5031579201aadd1792135dba45a98e74d124f73002cbb5c0d1b14f5062834619704b3bded18a767b53e5953ee4cec6d373eec22144ce0b0354345f3c06a716aeb6791e2180cff3d6a45d265e452b60694776e31e62b6e88cd1ab5363e5ac10e1534145b3238606e20511c17af49e6b20024a1742423ad21d57cd9f06172126ec06ffa2bcb71b809c84a2a756e656675fc32ffe48160b8b9d05469f72562b485e44a082f81085ed52911d0249e7dbfa6ac63d64d94455bee88324a1333297e63ea3b55026c19a8392d3a1641f13389281d7870f45b735228232fe595d43320342a0373ced6193eb6da5c65672c16bffa0436262c491bc03775795337915d32cb452b870ddb767a244807226f9023339a9a462bf32302da1dda71c632791c158ce12f83774a3d6d896e77016ce70ac8d0e761ff9f05039376bc7eed43a66661b922066905e911cf2dfa1b213e25506f77ec34bcbee63904a2960a2895b270cc34be128f00ec030938a53eec231a0802fc7c08a06bdd1eb9964b7e970a9407f6acb4402a79a55fff5e1245c716c11318f3ba049a35f122c089a125d07bd44d4f31ae4f02c69a14f95b7c20bc2164794bd5d5387326f71c378f264830b27e260fc12c4de84f566740023659494ba004cc51d974dc8c142d32a88313bed7fc0e7f246849a77fca508dacd575568f7f0446880c5deb2c561e145d2d42da130f79fe7e8a6b6324f4425619c046d593a36ec5c5f83be56dd80f9ea6e540e0b6694b6f273208a2da201bcda5e93fc68ab45b2e8f3947282780651670836ddb81f700f6e73a1fab6e534c7c13912217e2716e7f8a9f5666144940a56b8148e2a5be5c3dee42712cac1933afedd112d9634123e4e794436d20fb52e2f078795058536954b2493c43f4db37222e0912788f4e26bfa20644941c9720e7716228c6323506cea2f803f2d7113cd905572627e2c00ec5178e4481ee3a7350191057a5ecea62ec437948bbfa8212dcd45d195f868608a161cd28fc20b446fe0c5b2986dd100de8132138e9b91c505b65fa3654a6a655fed7c93c7d7213759e0c59301e7991645c603d69ccf52a501ec7cb654b23cd59d49a584c0d833b7eb969d524f45668517e6dde5c4ff117004954723de64d1c343ee62c6e50bc5713b3c7a7302492fc6ddca7ca0657f7ce57dcee3c7328cb8c4186bd276f25ee541ad4aa297481b6ad3cf7a0e3158ba0662de8043954dda2ec4fd94f8b4ebcee6419e5da5d75b5d9dc5faf21ab08327e81149fd09261238fb30fc15bf5229df7c274c0a62d393e7c311d709de816fd8eba50e2ec401594736a57fd170f362b7ba65a0cff8269eae67b4522f1c0467d1edc2b6abd7f7e4c9fdd393f8fe51bb0c59560d5d8173f07d40423c41efe549d44473a592eb853990ae6447d66de6a8589324289dbde430730be4c14c92a039a33717de6a2b901fa932e33a8d1460c2081234a7038c22bf0ca260bd5f2b501ce442a77d25d2626c0b9b95b8848471e0d9c165a9ae0ce4b36aaa572b63a6b1e7658f238d2a906447fbde114e3c08a753e46e016207e3406936adf7b59b5b4312d783f6bd4d6044eedf3be6e867c700de16bde6a2924945e4b7ee3021c674256fed2f90c79085e11cad79422667fd06c2ba5d80ee1941650ce76de2a35dba1532d016f4767e25c0ddbb1cb13d363101b0197454465f31915172eea207beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711174337b855d79a1f7a14c3882f200a29244ff62c12e22b207beadbd01fd979fd709b4889277e4ed90329df2e33222dd64a310c932854071d22ea8a757ea89ee103b19ddd14a0e6122e65156553d903e4152a245b6ced2a6f45e1233209a799c31cafd2656a34c443193d8c4b33bef0bb6d34a8844bdfe8971021b2370a41a84461a7dd1b4724eea50a2495ed0bc3096d532d05ba25b5d86e54728c316d8d43751e6e7fdb7e07a974208f3c0c58c5c2796be7b60b47f4b36a4fdbfea70fd0948d52256a5c6da780f6258f8b6e2f52c15e41ac2d4c1ef7b8983bf6afc90d68b9d20a80776a5d6807d37756483b794f94f433390e143354a2a24b286512454ebf7a645633b05dce153c590dbff850d66fb34c56d4ef74ca79536ed6ac272030ffcf74429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041954839a55a2cee97122126a7b25c84d48d477dc4f9f579e4d68b1c92be71a5543eb05ae23e5227e6d04ef9a5376750301a2670b6aacae6952b4fa982770f9841607bba60a931bc326d25c3c6703d2112b4faa250d07c7d96c1bdb0a549cd2fc21933eb8499f6aa04b4a50136a39bf9947c841d2737f37af4df21de11615ffa5158c1c3e6d8fc8a21fd09b2a6c24c4680171a8002ad538c87c168dca6e7eade43b4e8e9f795dfc42615362e220a3e0845aac8ebc620563012709e40a32387e7f4f37f1fd17122be81b97b82a0e2a602b342528fc51943d6c0b688ad00742265c2050a25135dadc325b2d004407c0e1325bcba1a559edad09345db31472cdb868565d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419e789724014f3326ab548f07a4754411c9d3c9a0f294b4e13aee8fa07aa6c317eb049f25b8ea8975ccddba9181da5732748dffe684b91ab1c158509092fc9ed208347dc78f3a9677a51e4cd78b0c6185397d1c25cde9ac366efb4df1507b365215e88440aab17773b92558d3826e0c56d7656f0250750ed0c21a099564aff502f0c68206bc44c2e3a21621f79857fff5605f2b36ee7de712a51c8ea2beaaa3c56ce634024eb204f1637bc7c221a980811ef883325aa66845d5a33840a81da8f16cabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711174e6c077a13f3de3beb27dd2fe7a7ee00810a004367d0ea0010613f1446f45633f50e404cb4592b05e1902919428e8474ba3d8159909f395d50d38c2b63cd203222b58c69b9d04d6f91ac8d0605c81003d41b465a741d88221cf445262a3d510ea07e0e4248194c5ec8fa840e45f6e72f26546e5ce8e0062bded8be559486f56db1fa282d174a6f7ab536ef3c69fd2e16274f7657a60b7762815d7a17a88ba41037adea666eeed5748c12e678f26f0e56021ebb59f5683c1cb46f9e71e52ead2f3543de0458af8c5e3fe42578355135372f15a400130c475aa2bb463d7a7cb119119deb1186fcb211d575435ada31e85e0488045c77685e453410374003f98e05fc4b0a2047c6b70ef09bb17b07dfb56bece20a4ec32a254a3cdfbe048b72772dc024b232af2c7575d3aa7c49de7a6d555a41824875a6337d68233a407f63ca62bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711177f5562646b8dee6c42e0db4dfc6dcf35ba6c486790a8fe4d273a0337b5843c61d03a4e0fcc4d3a5e85353c45f9639b34670cee17709b3a41d7139d034459b619d06e9a259a2f0f75cf42f023b342ca1d937a8901f29e9a398c4d8e7b1a95302a4d264a711032327ec5d697102f47942b2d122e1244e4465be24a50016c523205d37d5e47e6ff7f7118bf332045c70a335bb3563163d3fe1bf6f33323a2601838b8ad442d7bf7336c28d8577c77fe862791a7097706b16b368cd6fe43f09659045fb357419f5b642d58717c75f118bb77dc2b007506e40c6d0c6fbc7853ecf958c0ce16613c803d563dde043b60ab6b29ecfc6763ee67fe060fef294d48f0272c7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117586cab3600ecd2750528c86a6afe8e188f86834dc4a2b07975bde0458ec70b1ba1a44b5f8fd45853f16fb13640b10f5bcc90d21fe7e5f65d07ccdb3b12a6b322052e7d25f327206556a702046c064353bd65ba27f3e6361a374f913b2f060321af3fba7e14d4ba1afce5107eaaa44053a08bd80ccf1e345c3185117bea3c607ed20016177dfe2d5ba9639c0f90ca4e3305be2f02eb3e79001a2e772f46ee5d646fcc3461aaadef519a1978692a5aee7255e6d11c718743043f05d26ba7959d02c18af278931f75188c010a0dbae2f251648dc73e519db3299053602ccab7d94470538372fbe6334de29c462e8d3b71464df4394533137e6b9649af7020faee7bde85dd4cc877f35ecb858d7475385e3a592f20585d4c681980dc9b253eee4b4dc024b232af2c7575d3aa7c49de7a6d555a41824875a6337d68233a407f63ca62bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117b110f46981e10362be040746d526a73b2389362979ec9070a975b844c450267528795e156a70006e571c8215ce8bcf63c45cb60da396bf0e16eb7369e2b12b439bc3c54341cb7672a478f001d414f312008367740a95dc1841ca8c597b80b5363f045c3f8e51371b0c6d8d1fb0bdb57371ec0d2a94f08a1faa1ea4736e670e1602aaad2787d5384597a9b16cf3f82b6e6181342607d0aa6675fe4d27eb0bbc489c465419dda635423e645e5d9d94fb219afe4e0e4f9418160f73f9248fad8a3bc807562083477b1f0ba6670601498765e34a2d22bd5fc45f968a302c662fde799b10aa1369c50e5251c54d53a2f018092ace8253c541a33e610b4a504d2b9e004b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117f440d478f55c672a00a6d83f3cc03416080a0d0a9b50ea2de0d71a3541988e7cdad85b733c6b297259374a66d917e71cd68c702761aaec42e476844a5042d32eb38a5e02f3d38f469a53051d29d0b33391e1d6402611d3590190bd1ead75ae7914719c2448f4f870767ed658cfcc79034520e44c465fa40a937ac9383b2465439f57dc59a4fbcb0ec29bdd5fbcba1b256e8daa267288493e96ecd15c252323048cc7465f87af5705e6176f226b00221870510759190b3760f6099a371ef10c46fc30c436dbcb28497349c5190c733a71afaf436f24c3a2033a631d5ed1317660324d9f4d9862f309223c337ecc184c4a4d4f2941d911857c0841a86550c77f398a748f19c8f4176768feb2372d24da00bdfc050f55a98b6e324d5a593831381523bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117aeae42484872937e0702962ce7ebd152419c657cdf83350575c55c74b3a9c32bec0d2e68df50216adcc0cd0699bb2430889c952fe2943542527a632538b92b3d7844cd3a68e8d506dbe3a50ad74e7e4b05dafd6276fd16484a8f1f43dd398f5437aa3e56de86550aeee6a84319bd237cce32b16476efd665c297d2066934b34e2d148a76ff00306555b00c6c0b093e20fdeadd4be331be5ff4f5d410fc294014a244ee28df88a956d3a677519fa42a2a21e0716961d2646ea325f966cf25a6109402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419513ce434be64ec39a680cf1a118d757cce2080663bcbf7526d4041361017ee6d8e948e1441b381778f5e040c48f4bb6c0f61641760e5e03539c7cb5e44998b3eb7ea3f0efd61fe077a87730aab20a72b55611d74d72917624a81d25b1d796372e5d3450fafc4a051c68adc013075db0b9d7a2323f4b4414e26b9704afb88e479ceb0db6fac7733179618c51cd11d7478e5fae82fa0a2203dc8227b00ea505a208cc7465f87af5705e6176f226b00221870510759190b3760f6099a371ef10c46fc30c436dbcb28497349c5190c733a71afaf436f24c3a2033a631d5ed1317660324d9f4d9862f309223c337ecc184c4a4d4f2941d911857c0841a86550c77f398a748f19c8f4176768feb2372d24da00bdfc050f55a98b6e324d5a593831381523bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111743aa190e9461b7592e6d55030768d17d572bd54e4f2a0b300ce4133e57ab172f728b4e6bede4086d9b7fd642891f6c0b725dfd30622d2d3beb83a87ea3c1d6033ad3c26dfc26877d1bee9a7493ed064fc549346c86dedd6bf1a8e9555b43cf787c17fd4dc9b97231e7b17d4809c8ef2149b05c1bc7bb5e47158e1b79716f984e8f928711fb8c7c18008c6d01f60988771e9e1c375695b16ed139e171accee756cdb59922e4c10a46e15fd75e9319b0799c47366a3c8cbb0463f1390e83856e3167ea8a568808d4677d622f3f6040d45bbe631450c88374007bd24e48b6d5547e9b10aa1369c50e5251c54d53a2f018092ace8253c541a33e610b4a504d2b9e004b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117d08cda0bd6dc907a09fea5163f2b4b55e234e132f244dd08bec5da7950d66726ac1ba5505334544f0c92172df320c62dfdcf1457f6528770eb77f523ec5cad43f5b5d00f4edf1053f5426d3e01b9b80b67d63b66d681335b6c886f0c40c60d159e5f6b17148b491af15e085dcb1de4775003b4553f6f8002a4d31c0348e48c383eeffa2d6999380860c46f10a864a760b6d38208d4dd384f99f7070f3729e21a5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419ba83e429a3dc3b773993d60d28aab47a1347533309ed8f5acbbab7584d835d069d5ae55ecb49a07b8db4e41abe2d9a047b6aab7c175acc0c6a510506f15b44162837376cf80f3029da49440b1583d2429db8020c0904d2110aa7ca441c72d464b9b1be110117692e6ca089712f59140686e6ba331875175880cd3920350d68385de9f620403c9e725323444b7cdb5236f7972022b9271102a20ae3129a49a808ce634024eb204f1637bc7c221a980811ef883325aa66845d5a33840a81da8f16cabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711176e9d16544e050b5c6259341ab729b8384e26af5b34248951c7564421e789c176bed957062a1573232b375b6d72e56510fa4e5b7c6c3e59392e2ee30d9644350e1f9f2a4df01a046ebcead246d571b82a7cbdbe2b96073a53fb409d32390e36413116f65d117b1706f9e0344f3b2f35127feb6106e213191549e6524e4407b311897db306ff924847db83c31f3d10de15f763a055c908a35fb62c3a7b79b4cf52c0505b1968886b72f428f00d7bc40635982f71051454a14e08039e5c59417f510c268e3b939d111529c91e3e8dd2e93ad48af72a79ae5340b38d153e8b4fb250d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711171a7ec44f9cdeca3d388a9c7094e1fe1d925f791b1719957c43599b3a3911b74311477327a1741e6ab3d21257afda1c231d30f21fab3e084976f8b46ba4c2fc1c1f9f2a4df01a046ebcead246d571b82a7cbdbe2b96073a53fb409d32390e36413116f65d117b1706f9e0344f3b2f35127feb6106e213191549e6524e4407b311897db306ff924847db83c31f3d10de15f763a055c908a35fb62c3a7b79b4cf52c0505b1968886b72f428f00d7bc40635982f71051454a14e08039e5c59417f510c268e3b939d111529c91e3e8dd2e93ad48af72a79ae5340b38d153e8b4fb250d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111778074a1adcadf262f54f7900bd44a70f67096a78aea71b2f9b36ca0bc642476262880074dcd24f5855006111762872017f4ad8186227102fb0c1c421f43a9b1df14d78193b05f067fba17649ab9f7f102799e0095d2ff5477b795914ff082d02e41ad40686fe9826844e3e7da789ff1d5fbdd506030f063d0b8daa116c34cd65fcac4f74ef00135eabc610093d5dcc17e85e8a5a8149ff54de952e54ff5ab828bc66981938e2d020d6576530918e3770e62e956c5b371c5f65f2fa3540ac500d4920df18abfe0d287d36ed6f439cd76e602df61fe4948c17a920200b5733a30ec5bb03589d622313c3cbb233c9d050533b59145bd15fe071eae8203efcec06611739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041948b4b160f361687a9bd1832e34d7496f1607d84aa8e7227ea850842fe91067079ff6560bac7c2d3557669731bef5e955782a075ee955710393752d5e26a6667b6665210f1edea4233afff577a0cabf508f2acb561e0a726f55c92e76284b4f3a274043021c04021511f661114a303d6810f53b41294b780b7adcf0294a2fbb525f7c823deaefd72f3df98954b4dd69744216a0776a1c78179adcd24df77edf312efd07409611125f0be7920131c169461fc2f132bc155e2d1080e42909f14409497f0a02ae3700063a95ce0d1925ff082b981c3f8de656222cb4135d0f344240c5bb03589d622313c3cbb233c9d050533b59145bd15fe071eae8203efcec06611739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041921259c479d3dc00da4ee9574f634c71f0e87fd30457f2d0b2d72b50d9f79e67bb2178e42b798d313e279d931daf8b926ab5809031f368b21ce1d783abeba6e22f76e9b6a32c6b77dc0b8874216fb7e196d2a516b5267bc50c1a1e1491e4d44381f878454832ce06d98d55e050dbbc652de398665ba4822490cdfe457c999d60a5318142244e94841556e4b6f93b5207684067d6146e4974854cfbf27467f345f5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f3a63c4be8bef3322f18142296f50a480b198e41b7f7907742e3a84cf9b2ed5ccd89f2730a3acf11a955f81f246a610790fd0c620c219d067858ca1a86ca3c08831f7b3fdbf51a684f50837b2ab51b3f2a5cca1c1727255e2e137a36c6e0ad1999a172033079cc640c21030485f1221263b54b5113cea2045697b27be320b62557fafa78dd6c2022a817a0083592c40233c24f2773f4604e35785b0c932cd95fe69c4a0b015b8d7a2b898667d98c2926cb41602f32f966422d8e671050efa76dcabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117f717ef1769f31b4b9db54d6780e71d033325897c1ec4342685af175c7464a961a9c6342c6e506f2b7f038210a501503a6bef924d77239d0fe63b337b7d0e3e0157311a229e38f0155341a10284efcb13976ed4205f26be710deba12bfd612765c79bc453c4a4fa6f14ed4f68fdc4422c163c79751c64cd577abc82112294f61ceb81f00a85d3396af7d0d07155f07d316999f47b5cb5f15449b4f02064a7d7172d037d66c2263531805f9674d8253376aa423c2b39f88660a05c440bc249bc58eec1131a854d1e705fddd51c7954a40b7545cc46ab2b6f1ab2936525bd6d651b108a82641d497450f0d83f2c9d296f17c5b1e47936aa0b50eef66a4123a9e4606d9e352eee483364f107e270ef7f660ec8593338cf7379150ac8b97181f41f73a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711178b290747d00e4a6bd1c6bd5f4ef4287bf4ab560795e9475e3a78897214f99753a3a6ef234ce23f7e730cab4e1038720bf2b9761b63dbf215c44a84107cfb9b57555a436ec78b666dcd993e7a541802743fa909741fae0479834a644f4d95f73f77df17673aaa943d3b75db1275458622e850cf743e42ee18638b4d58e0ac7b16742869292d7fb003ad46f01a84018279ed1cd32ee63b4f5f756d2271b347711a5593f328009b9f40b51afe678b11fd59de52c771b1b52430e8bc5b56fcc6523c9502a33b5a1bd2209a8b3f7861f30c15bc918528cdbdb60ddf062660b83dc077377905542c962279d68ff736b11eb20da2463b206ee03051759cd2702b101e642193ae3670f74512d90ced603cf97a054286ac0d4fb4191fdb32ff6c4983087e60c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419051ef913c6824e3934e3046e94e4ae143b5b1c667ea19928d3c9e47d58ae34684e8ff41da1fac757e65f0a6f2874791908bb3e7198545c62ac472d3abafe9e3e2e07f773e4f0464df88fca067bd11b2f0588b9000a0865137217c9354e2c6d3ecba4a70b87adb609d4066056dbda0148232de8681d12dd741a5d3f0efc25907ef86e5971b8cebe23a7295b7256c58307c9144b7b71dcac23e9853f4f1880e752d28fdf372534635d6b757241910e30083d71a31e126e79340247967290951f70a0336225eda4033b23fe1807db32c013e713bd758b227a25d181ed46e51af1788a289c4912b50b5e1cf6cb06205ce2644f1e815375896a0021c7417bd7b344425d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419512c834b0e35207ccb726402b371d34ace570555b6ad5a6a83a7e86147ce8a54cf23fe3e0f35a713dfe050788919d84860aaa871f5702526f2796d11ec5267173869b320b0a84263edeb12595dfdfa774b83cd6a79bc875e2d79cf15fe83db1a039cf33f6fd79f5c55605502259dbe49cff619764928ba2474ff4c16afbd5f1dcd895738d84d1241a159af237a12090546fa53684eb6f30074fd6735a034526296ecd53da3a5982d7ccd5319da5e5d19f140730df97316280d9e9d759cb5fa4f462d802b3b396a3f3ac9313c4237ff7a41d0571e3441aa5956295167683ba848108a82641d497450f0d83f2c9d296f17c5b1e47936aa0b50eef66a4123a9e4606d9e352eee483364f107e270ef7f660ec8593338cf7379150ac8b97181f41f73a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111745d56168f54fc72da1c696290c6d8e381aaede60f97ea812d6899553f705676fd7c026246ca3914def20d966d3b72d1b35cf0844fa0dc472db858350876ab33dfd4679161cbdda5aa05b4d2f0be900743a41e06a132c2e3775a36e73b2418f6e2dfd6c5d946b5a56921f80357c076140b9c95e1416a45b0557702f306296dc7ec7f1fa217f32cc25205e1f04389a3308472c004c107513711202247e6e8a6337a82cef5c9fd1701516da482e479ac534d8f5ce499eff310f9aaa935b8d8ece189402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604197f26b23abe27a0775ec4e069bed7e81aac863c7ec2157e50edaba1090e3278086446b024aabfb27b31555020e3dfbd503534536e46f18c36ac04be5880a12c31fdb51640f9a52e29d6194641ef46ea03650c93755aa92d4c638dce7ba9a4511e09bea2741b30b612a12339119f271570b639b8235aafef7087e2385c4ce33156a0600520dd85354dd99a3c3019b2d22a9eefda223e292360b0d60c18be4f632b02668a2d20b1827ab9f626709a596d6270eac078475a695816572b3e186ef6683f86a844f67c222b6bf00365a11d1b639d051b1c10266260659dc87e5664e00f8a289c4912b50b5e1cf6cb06205ce2644f1e815375896a0021c7417bd7b344425d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604191e7094348888ae230fd6e5410bbe0129bd63c732f5a32b775d66204c50b0734966b17870f84cbb40df64a355bf9096553df1f611ce2e8921746c622d9cd7be6dd1421c21cfc2353595b529080a61431eb965a9544281b7680dfefa04a92e7d431f878454832ce06d98d55e050dbbc652de398665ba4822490cdfe457c999d60a5318142244e94841556e4b6f93b5207684067d6146e4974854cfbf27467f345f5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f2b7e564cf6a40641861ac13dac7fc189da5d0027f6d3a416733811136ee926b47118754a4b31022f730400f6f10a41bd105a8509f61181035a89c79a715401fae5e6740830f8a314ea2346361600f6171eb170ec74e151da51cb656b25c5a6610f5945f4b87060b3c364076fa59b65b0122752046feb80b6ad6d1405f34074d9808ac7010846863547056193035c749eedc2f036b9dce2d4f15c169d08b2e17cf08283786716e71092bc741a1d37d122e112809896528311b264b31f60f935b4533b90d876bc9072e1df04c8af5203d98a55a5d3cdef37409f6024843e44a7c377905542c962279d68ff736b11eb20da2463b206ee03051759cd2702b101e642193ae3670f74512d90ced603cf97a054286ac0d4fb4191fdb32ff6c4983087e60c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419c0e8d736bb6e3a131b341d480844c15fdbf8562c0cea134a2fc1b60b115e234661a626186fc6992e916b4408eb5c57061400a253448cef46a348dd6ad024e03f8b7e185e85c80b7485a9aa161628cb546edce32743f13b75b63ebd45c4d7184604a8f420d4318f23935f752cb45eb071ebf6fc4a4ca5ea78e5f0c027e0f21f71967a7c3b8163c61f6315ac7520709b56169fbf0137fde2552759f35e3699fa1af6a13e6000e2f54b12f4b054b695e11e405ecb63eea30c007c84527c6307751646aa214d6092eb109caa1b0ada7e230b3646ce5572756760b228ac5b448cc4175afd3a54cbfc0812995eb9068ab45004ada2a44ec5c5426f56b27a7bb6246e4b8415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f642940714b21f2f853f126e91974122fbc7594b9d8c2d63c9df627c8de32652c2acac2f1a608634b1de38773a0c86047a7f8f2d704779577b3668051b01366d79fe931cfd426f7e9c46865975a418161d2613162d0f5b0843eae50420d0b3102dfd6c5d946b5a56921f80357c076140b9c95e1416a45b0557702f306296dc7ec7f1fa217f32cc25205e1f04389a3308472c004c107513711202247e6e8a6337a82cef5c9fd1701516da482e479ac534d8f5ce499eff310f9aaa935b8d8ece189402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604198f917f3fb66f9333b829774f6e53ff505aaf0532a2113f1602b0861adff40324bd17be55530dda13e7ae5a2a1b37345f1b794b60d33cdf1c7941356dd0449e34ac284356ca2be609dc09355089bcf050c833e2419fd29d04579c537457300e7062b44d73ce06b77ab4724339c0578a059d5f7b3c3728456907b4513b5740941941067404aedf375c65227313d194a84efd81b17d750ee35824031914f313b8591eaaea1f9e62f210ce1082085c3589364a0a935d0118f45859d6ef5c9214384b21b689620631e27884225237577d9e5ae6c7430549ec8857e21ebe7a7b0ffb7941f86f554d7d5d12cb326f2317afdd06947fc4247cf0d8461b2c0a7cf34c2479b0e1fc71e3972b794f6c772ede3caa3223d8c74f24972f433f45885762b0e454886bc44b0c231224bfdc1f0592f9a04433bb867784ecc76c5e00142ab03c416548a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419d6f4242a0ff2bf36b1fac55984e48c64602d9b35826153045b5b0021f8eee773bf1b916601bbfd2940e9a4246856831635b9950445988a66a4c5076c36b2d2248c30f00665f5083b3c5bf24efef9b6727188ed24630551237c548928983b577d9375887975f34f570238a9047b7d133b4329c63bf1816c0e8f285761534417258d9fbf651cd0827dc93d396d207c4e59aef0f50046723b19a6685e4dd0d34966a833094f117ba969887a12142ad6a15de6fdd60fd633f24bc8938a40c8d9c700ae69e02eef0ba753d1c645084371bd5c81d6e3279ef84339d11eda152e2eb312956062001ca8837e9af15a1d6d05e1584502a204599a72087c1d4e0b1e11e93d982e1d311357273fe4e0f07367bcfb4bc954dc675ef80c11b9cca02ea67fd550a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117b9d5bf2bbfbde2374a476d710f53254ed590a36683c90f005b522d748aa2d36651c4365ba7991f36b1e8a264d948c04ab47d9a4de437d64299be2c08c5098655953f1652b56cf819861ba02d107ecd7ee52258284ed34f4e4924ed17f7980811a5de665d3189a76f2f48e7220346215a420dff53171317384588411276b6b731a560e250a5a2be5677bfce0fe2c9d44cf6559f676027036507662459fdf7e23eb6f5405a1793ae718990161d7f56df1c0bc2af2c3c742545ece00d4fbf195066b5609b0646caa24ab59f4a7e1b4b49734b42545e854551795f04852baf90c311dca04e1ea86d5d25486ab044026f3540eae5436b351bb86d22fbea6d469fd03a1b4ced35e82a9624d6625266311b7a59a3ca29572bde1704d6a46970351d0a51cddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041982087d11a4b578635e93371352033639346e412df339790c0887fc76d37b097c93a39e488fb4ef5588e0061574adcb67cbdd6e0f7f03a677319f94520613a365040d2a14109b0b183d3e79783d0aad55d9826b6d70f6193ffce53e137c68e87ef4f8151aec93876124acaf749800f957087f7b14f006662778a255492399e3135eb69a35e3ed8424aea35109fc9b5309d4ab070db804484ab937f76b6a8cd57e29c1d311378fb04cd9fa4f093963b54756f33c62fc5b5f699a8ad74ff255d8717638707122dc9822bf95dc600df78265e62d6a1f6d8535283a0ebd541d6b445d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041915087a58a9d0fb287aa8910fd5ef8d04fbd9112945c0b74fd0ceef183d8ea77ed66a0d707ce4046034e17939daea580f00188a7c58adfa6b7256ce46e5c0412c19626b4d6be4bf12e49d0e2749f3f3047f53b1252b513e62bc77640f2574ab206b412349c8d1352bea0e6b061ee6bd76099fc02fc57a0330e295fa5f7b673044f1fa672e2d0e9e7c5fd22e0947220e144fadb52d7c2f4c3039c945190e689724b24458031739f7273ba6f459adcc1e197f92492c5571855993d9070e2075071c25a89e342a601b096826fa0db5c8e22386a900302b68497ed5b3e81c42e780429f456622e207987e3b648451ad768f6c7390c957565ea821c00097565dbab34d1b4ced35e82a9624d6625266311b7a59a3ca29572bde1704d6a46970351d0a51cddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604191bd8b533d6603d2559dd602818a58e717d7445580a557c1c14b99c304406a00c5ede876ac008ff1c48244840118b29659565e241e0548553ce4bda7ce3e86e799c270a21a8ace9596c2de478ecd14372b840c85441ad0f7c7053bf10b295065e7ec6245c51f0a32812fbbd2c6a102a32b777f06309886a240f0732317312a442fa7f160a1b43d03eee22b7155d95183535cf0c78d403d42917b75f7221c06a73d5eeea1c1319af44f5e48f2e7a5d5c4c220cd8699e406172dd504c32f2e92c77900070274bd1da23c22b556a4b80da0a46f8f66d81ad814cbc56533faf4cab65d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111769c4a84441a87c133d0e666213014674b16198476db8d61481e52b37ef20ed0f6cab674fd71ffa059b23665430aa2734222b923beabb83099a80c624324d0c00322a7734bb38ae231bf1024da578d209e268c84bf1e76b653a80f804ccf5d531fb5e3c68ddb8dd5438ef766937f26545c5db837a5b8098067208a66a142d914c9ae89a738b0f527de0f6246f9765b06518a5d73ef3e84e551c0b951ded65d55d1f287b0de8993841e76e7d757b945613d9d20970e1131164fdb3ca3a28fb257a256a5c6da780f6258f8b6e2f52c15e41ac2d4c1ef7b8983bf6afc90d68b9d20a80776a5d6807d37756483b794f94f433390e143354a2a24b286512454ebf7a645633b05dce153c590dbff850d66fb34c56d4ef74ca79536ed6ac272030ffcf74429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604199235ee0c5f0848107b8d615dbb42dd3ee491ca37afdd94213bb6ea29ac6f9f7de1e3954d8700772e9e707d6db2259c15a329ac7b5355122c7650b3346c868c70ad59726e7d65b1785e7e9435649b42505e1ed07d85eca0289ea2db2f13d9b236eb1fdd6c9ebe20106d9cab2188558b551f7e326f2d44b07d71b2f16a8f249d68ac8a0a0c3dd26954f15844387bc92b239e408d2716b8337de7027d056627cc19c4a9a663961e347bfa4fe65d9a61e75a3ff5c8047cd26f0ee44ac412fbec7134d2a583501ee6b81ba444757ba9fc9e5faa003830a9b09627c99bfc3a4c7ae92c42cb6806ae37b00f528f9a1fdd96e953e9c1a96273517e4e5151015e8d898d3a740057104e8c992fa5aae00ed6159477ae964e30e7613f62370a803901c64175886bc44b0c231224bfdc1f0592f9a04433bb867784ecc76c5e00142ab03c416548a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041945484239ce97064438bcba4e52685c5e5221765917f1dd75825f4879ac678608b46dc1708e139551b944375f7c476077fb328a5db0ce3a6386ad3f5a2398817113584439a836512d9f2314360ce9733543ec4904888e3c33f79d555fb4b5fe419f352e667db5b4791e56452ae622ef699b3bd8600492077dca85cb3072f3743d1465641b4c8a7862ed6e0f05d82e7800ba8dda4fce0b3b62925d6a17ff89032e12ab92713994f24372eb09517f6f224abb89150faddfe1094c8b363c526e49446c5a5d732d97b32d3a38f108895cd54a14009640269bcf4c17b1c7398002502d956062001ca8837e9af15a1d6d05e1584502a204599a72087c1d4e0b1e11e93d982e1d311357273fe4e0f07367bcfb4bc954dc675ef80c11b9cca02ea67fd550a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117317b7f5e031d8024c7bd84441ecfec286806507a4e7994686ff6e92abb612870b3ca7e4d6b94b0423b6ee771148307603419a162b423483187bd664ab4eaf40ed9ac2602f63e9d46ec154154a895804e6fb939440619030db71c102aa0dc1f4d3eabe5495203ff0efb917d73b6ff5b6d1ea5e711bccf02055a846e01ca3f8428c2799069897f090e523ea639ed592968fad05d71a98712460c08f61b4a39e6225fd0f331ceb11908f99eb346e082180f1ce6b42d025ac9201aa8fe4ac4929e1549afcd0cebd66b1dbac68957eb8e365e3f1bd36f1b7f473a0f346c121ed5ca38337ab21f59607a514fa1916ef1fc300ff2e54452a609532bec0d2e329fe8821f579bd32bb8ee4e51f9bd792cd6409909ad0fea00c778520b87c43537e205eb47b61c976b397d645775b7315039bb8b0038e6df21aced3a530240c9038b5e867c972492631e4a27004bdef306f0932249e3fa8935d9bb0430f085206a914d7d7def04e93f9daef736c9a4e72f5477114b981b7d35b0f1f85280470e5e682a945860f5b05925cb33112ea9777891d9b57543e3512bbbe5f42c4d02be4cfbea1330938c576080b11518c80b863d1875f450ab4c906ede645d453c89e201c0216924b71dd340b54e6141c310fc3e8d35f57bb959a171c121275170d43d55bf279e30292ef73fe1c68957d9136e3e251062389ae9605e812bb709966e737e193dee134146ea75e9a57e447a957c732c889b5faae0aa4afc46881602e0fd085a48607d502abe18320d03530727e70737c5af0287b64f3b5e315635b4b27c2ca67ba309b1d0d85f362a3e535ac20c591ae54924bbbcc868863bf0184175035d2ec71174cafdf54b8efb5848d9608458a4176516848b310ea357db6f8c75f6284a0e3d1cd66e4e1b295e877a3e97d50405dad32d36281a27b773924ceae75443804f7072ff8245465991772addc8282705d931020a90021c9d688515943e6804e76a0718097d131ff51717116f65fa2aab565410724c9430a2788d7b624c21144aeced4dd2ee2050d289ad4ff39a7e752be8dc0110904351d6571c1b56c5624b1a6013390121a95c38a0944c17d45b45b593a52531a56e485c31980f0eea5924b296f47b18b97308ab535a7bfd55ff437d87d46126d6b03be97cae0bfdff5178ede4902dd56f5213e154f26ec3455e76847d747daeaaeb1077ee2752fffade48009f2c2ba1ae642537779f3f42d67a372d978a2ecbfdff340969c026eef6005ce903e57e23ef1c129192df02bc835c40d3648f1788adfb7a48e96919a8ecb500ae94cd4253c02909fe0f38295eec300766bbe55c36721a705d52d5565907b7421910935c0501610c7310b324b6d68e1d7174c24bb00a066f7172721c0006ba456559f37ea2516f64eb3ba83b185cca0f3740261eb3f2e223ac58697930506623b37021290d8f3d274204770f2e219a33d1130a34b36a5458619d512a251fa25dee46882ae8255b5897d2551d25d57c2ff2b2be4ab7c02c03b7e84a0815eef3670023423a7007e901caba1f7d5c7a6e3dae0f0c0e48ac1a1f6f71eb2bc414dd2df47de072d77c3c7d82f8061ccee00621d22ff22d75ed382bd5f6a21d3a0a535c2e737b6b7a8f4a0b038ef64fa4dc551afa7a764214a2bf5ffcce4804e3e4340122a259412efa866e01c3503ec1ed4b0c9415f47885fe762194ad8a30219d190035a6811cf691232b392eb651f1a17a379770ae5290f2db55bc2352594496bc393f2597365bcd4875556b49322909c773ded3b36320e8250baefed92ee7a2df247bfac04e57f7465f4d4a88019199241e627ff0475c2d5e3a0c0b2e5cb05aaf06d1652f21460a6557f367d3672932ac3815b7453a97665d269e22897104e18145f019d31a73faed3096bf6e244785514e0f7d5537d3cf332711bc8f5cf5295a410653d2419f019f0a8ee5d4733acf8e443e05c946a1d814359e49c9354617353d6bd2855cc4a52004cd52353599db352b54239a075ad0004ef763b835dc388b5024aa7a2b403d6e67f2bc1816a783cb4330da044dcf420203dd15c32083b92a37a646964031ddf376cd10e4599988174fc30561765048a565b8816b39358aa06a8622d67d2e59043dd5a74f7b85a18375688c637eb635815c9bd1175bebb3cb5d3a1fcb2aeaeb441835545557b37a8e16399edc17665ae7251375c307939fe560fbc1884fcc5c9467d897172aafdb27049215f645e6e7c809dc52ff7cf320d31c35481314d08d8a6183e4e7006d380318183ef54d32f5fa3c485e98382c7f67181175b27ee06dbe2c09d8f343ea9aed1bacbe3b4977195c37f3682104aa31eb41a87c941db19ace7a603808714764a33914a8a82a9b02973f6ceb0f3ce7f1b4739e91ed2937dd4f5d495ba52185589a67e80c7c02a0ef4a4acf99dd10deed484e69bdc359dde93f469542e927ba9a7440688cfd7b9179ed224213ca68b350ac6029df6d2bf0944918cf742320c361fb28f58dcb31fbbdeb49ef3f2560666bcd5dc53c6a6b7648a6430c510648872b757bb7ac4332598b83693caf3c7adc5d8e2a5915b50ed3281365fade2a4bbe08271ac2d89178154ea071579bf1408c06bf4d69deed5bd5405753f814405f721ad5769e4519368e0702493b6da56c2046096c3f1199760559e01111be6a0ff44cec2781493e7d5bacc7594f91445e716daf2321d05c2079edce0bb92b2f211dea1f44bc7b6149c3e7fd00e617843eecc8050c8bf7c205cd8c7b247a7345549386156e98da5444f5b681509f02f2484a3b295bb577261416faf50893d0222e1c19267e8502702b20dfa044f239ba284eb0916c0bd8c056967312054093070ff9ef1d36eda78344866a47321fe3150533f0bc4b8c9929294259944630b46069fdf23d20824b99579da90822758a69236b144a1ff3bd060c4cbb4f2b3b705c09100d7e7e824c6f60dd6fe32d329fcd6c938df554f20df017fb90eb6860ce34039f08995b190efe158a5d980325cafc62c7e97721df6d2c4ca99fa5037479dc436c7124047a78fb2d49bbbc1fc90129765dc34e1173d889583b199b3e5578385a2f6e4242def37500bf30c34b88cb735967afa97300a8ad09c874b166617927428ec700393210fc53aa39a77c0c5d993c8e43437735e1567bde5d5d6bef534a73180067151501d673b2243b506e31c85498854253d03b622e1638b90666518304f598e32d653cce76c2f6384007f6df250ffedd7866265d78cb93283b25faff245245e82be4fb927227571b46d91c0e1e64923670a21da04d8428e71972b0210fae982d32c8020d0df82913372c4a164ef97cf46619a6e45486a71d4b79b0373eaa9d8f7ba71b140824901263bc0c491ab33a6b17c14f73330ca23f70e9657f63190dfe74b87f4e30c1014b662736b25cd87b096a422aef52f986dd4052e72e643bb7d650c92360160a51442bec10c27793a8cc1113f4142bdc96951f09ae52411359af276cf70a665bd21215be375123ba36cd195ee07c56a3e654500d36c8720d37592a5f528b03a180d431b4c5bb32106f634145671807bf22d8407666d50f94b054375cc77a36c2618a42f7931317cc1eeb3dcab6f158e28dfe32d44f1376d60f744ba3726c437367ec6fe6707058f196fa0ece1d844017d5930510930b30ee6bcd014bca621c0bea33174c6bbe7ce28c5055b4878d4c7bf4c34fb85579143e3b0945425e88382307b41d13c2ec58f981380456f3374c1453552578dec81b69885f6a161f3720f494c711b9ebaa197e17ed23c11ec26301e17a41a4610d305a9d0c1417b7ba4298065512267fb305bfdda1248ea21c20e9c88620ef3c2a33a5bacb1fa0d4cd034ec2eb662f18c22c40d6184da7851e20945f000b3df6d13eeef3c64dcb2355188300e459f589b8663ca0fb6eaf4f4562bc7f1f0622c92733d706646df090ff2a49aca2409bfb185e01d0900afd022867c073e10de2d51f41fc1ef37a1e774c7e43c7b3594e35104503d36327d3cd575162845b0ae7f90354ce3c68094e29276154a2f1524eb893258c9ad71c9dbf29349e696029a652fb3ddb9bb77ad909873b34746c7e202dd122f76f5e3c9e51385eed80217bd370d4042b058c35b93cba5378dd8439102a3357b50afc3af683343ff72201210dc9ba5ca6e19453ccebff746b7951017770d251d58ae209648eaf4671bcda06532f4200d2c89f670aae38306164f32a49492747173ce167f9631843b973a64dd340a3157a5a6c5988d0c67a1b8cff0b51febd716c4ce97140585105ab52f32becfa9b080907a739a6f8724b1cb91d0eb905720049d64a453a8e32273cd3e56a54f9ff7a82cf97128facad44c634b0790690254304ed2c41ff39351487360e0e525879170f85685601373d30a8b8c35de93696707e67a95d3c317823d6288a663b1e86369901c54215b4916b34ae377086b621208cf56147f7a17971614edf7bcb3fe066b668df06e8e5fc3dd8613848cd1bdc605fdb075ad0265471b315154e24aec70d97c47a0e39485b692c91f2150646d1272d936f2fb6080d499daefb78cc4cbf5dda7fad30399e2a542d442f6386912b3373c7d93862749e603685da5ab63eaf12b8db1378631b3d5c9753752cd52d665cf75e3e651e1c134b37191466cfa963488c731e3e0c9ba15ac225a9531736395e036048792d52af6ae7631817dad3e8315746a711e7b35436df25686a9376c2733e268309e7601100462e743da23d987009200841e8ef4a66291ad251bf6c2931b543487680c471752826ea3c18286c639dea8f3af9963109a6715a7921ef57236ddaaa4d89c8ec5a0e4a54216148c744e2fe585a2816c46f1e31ab16abb4c57e22ba7216e9500b4d5efe607b9fc02606711f487081a3093370c4cd4ccbbeab376413c33603feda310d7e33088147821c1b3dd95652112a0136fe0b3fafa8192dedbd5a4a4a00907ce46df5791ab0bb1686dfbd0b6c513e665d69aa2ad3da421bbeba4667e72c011b8850fd17989ce662af252221e8d14a130fc85843d22eb212d002575261ed4945396c1e6331ff5e7a179b590124cdbe3222ebee54ac33b3257369d948038bd932da477258c5952c5c801a934aab966c2ed88630352f65817a6de2ea3db413397329495f23da026a2ed8eaf514654ab30eb2a4f812d16eeb31312a443c22005231c02fa966ac0dc32e60590675b22be9254d7d90067d0c9f3f4b2cdc3cd0a00c305daac10d6247916c5bd7685ff841052ae19bfd545f4dea12694dfb51c956e541add536712c2d9e03a7e7ec30403f7960903ee12a3b66fd7d05aa8228bdfa651f57974d62636f36229b53153daf00d77001cd3d328d975a17f2335f6166c7186552f8950fc974f226384a065a2d180e34faeeda08e2af6724330ebe4b8dee042210c7bc52c2da6f1c71e46c13a5041b58168e6e0a106cbb06e7cbb338dec2e068efe8501040bff67786e68a4e3fe9ec2a26949568037a2e019ba9053f2e37bc7ecf46ab41e1869e355f838c12d4565735e3882c4fe8e1ef4eec602750768bf92c14d207178a9d7733e2816675d00c966d172e11001d46c144d1f3c743d354ba3aa41bc938c1ebc61199180f782e4ea05ce972da783ffed071c9f0ba0549d67f2a87045028975ca84356450e5b08b74078943561105a847c5d4da94f7816ab04347124d909088c4100529cdd6d32d7351d632da31a449556087225e74b3a5cd5557ce71125d306f104af91b74b1a65363d155b5a50cfb8cc1d157aff255d62e548fc237e18d726465d4bece6109a4a0c58864a585ed1866f0e0d0bf702e83ecb651b278057fc353f6bcf95175a910b3618ad82fd6366754a798aa69c7b90b8511aa6f4f2167ef3e8721e064366de4e5d0fd79ed010026aca52b8e2db34bda0b8566e09f45bf3d94b13d064295d58bc5a097c6f573d5cd8962ea4264640b70bab66c099fc61eb65f94adb54a6375747b65b3310124a8218315c76313976d90928780d3541440b82294e72c9ba082ae26d30e2c8d27de4223d412f32997c6bea2c68d9fa31679951d1628d359b316cd868008d1d7d5d3920064d8264e076300d9c314fddad0abe8c7b5e44a9d8371b6c0328d74aba5a3375fd23e5af990b66686d2b4ada57409048a05a72c8a4411927461282a3ce06dbf04e3f7b49953fe374e2684cc1f40ea9313c72342cae3c39270d2411589504c9eee655eb0d6125750b241cfaafb6582c19461554cc5913a475c31e91a7ef62245972594b7e3152ba04ad610bc4572f7cf2294d84279a6f93110c082167f231e31fa044a738c03ccb98930042e9e249f5a31f56ad1a834a8bc7287c34fae31af931004e7817a6669efb8a50780d2f7478b0ae7837ca05561737b1733c398a3be0b64e378f1f8e3f95ec865b83df024ba92b3d2f0330e92a22f4ef5e72f5473aeff11357a204aa4e0dc9c840ca268a3d3e4ba0254755fb41826d3a46cbcdce38bb48ca29f5dfdd2bf01d732886ab82733a1466368f004664da38a36eacd34c2800c1be46f9ce782a8986f33e08794a653d1d723bca58692739963d285c8e84106af3de07b28ece7669eeef6f725cb70f7095cf4d83d4f5583d713d58882f596310114a56f0ccd04cafdf877560ee25797d5c3a4328cc310d6c583a1b94fffc0eee06182aff3e906b5014454f9df338457200752e20810020f3d43c23b781133b1a14ae4b1758bb05457ad1154e24eb2ccdf6541e8d8e720f6058ad77fc42b804db3b12181d02d7444e53e77537589274da55450bddf3971f90cc6c251f8ca67dc29c15521f2312526cf67611c0367843798809151843fa428da6cf6fe8fde150c583a70c40ae5b02d5f5f73c65db61396324ea6a5e21a9660aff3b2648966d0f66a6d50f0be1754565816c55b054d6249d1ad902f650e31f23260d50a28da76b5c921735f3c57c7575de521de67ed61ca45fdb75c4ca11074307f24f1afc591fad0a496fb536ad0c705e3f4cde1177069d0da7565a533b7cb54b5203381e0171499ec5556fc66736c172253abe1c096b0cf4bb0d33a2742b7524011271f524786793784983ff5e5cfc38f517af883e630a75b71330f5810965eae129acc853382e25281cfe19402a70f4804c5fcf08650dbd347b37b5b35d4047935d1b17137a2b37d7626f19cd273849b979161ccf10d38d9612de1cd27d79c686333e9b466d8efb7830d76ab04ac70c7211d0f0af6f6447083f7a4a6660ecc83554c8009726be54bb2c7ebc312fff7b9278c5f66d2e6c8be101cab4000f6f08c2103acf082e84421e71dabd80787a566a7ba2445266b4cb31447af33f753ca6ed03c00c4f74307dc4125a57190feada8a1f4d353371aadc0d13005d040eb1400766d757be1d0195ef50cc98df3d6d0a1a0d2b8e195117eb3b627c06c4151975446f046c3a73f3d49d4adb1f5736fda515763be94e5393b3c8574ff35e792e81ac051b0a1711bb8bdd79e5035278ace7e902ff3fce46524e7c18c2532a6b29343d50e5f9de0db5782b38b2a22c51c43b8d2908eaca3f5ec9720aa703ee796605f82a2a91d417d6b3c00915e3c5281e9688363e9bfe4163a0ae048242ae3c229e87020bad7267373ae35be1e1c8131145da424a2f895c439a34141496c948bda25328edb4de6612c5562da62788183c946b00df7378404f973012004ca30ccfd8dd267816db6192192228886d0234f942e57ad1fcec79adbefa0d6839b639eae3cb6fe538bd1ac48e204686d74637342426654ba78047a38a4b73ce5130647942e21ac1b3aa64f1de5e6da624bb4a7a96712374e4491ffa464a552bf6e960c41eb717cec4820fd44632081129b566c51a9d5d3f343025339db416e52e843676a7e77914095142ee0e9840bdcfb30360cf4e59e59b54129efa3b306228f50733ca5c331937c978a7e9536b83fa4706b3504608269d3936fb7d8f5cabd5141b00eb4e24e8662132b94fc72484527303a0b7cb3c0e330c1d4e2a970edc9468065b41ea3d6afede156a68436e72aac60174d96b36787c287ab34c42767748453a72f12b21c446740b43c5e375e05e045cf0ca6b42c58d4e1b5e3a537d207a385e57803664651eb60052fc20594e9d6f39c486573b669b42183014151bcabf2a0de8f7fb300f53a22c3d1e4d3ab2a3761e6e78a53ac54dd871416d7e191d2b5f6b52d2fb0d7aa6975bfc6f21022a13b3259d828f4490ec021075a0eb60517312196a05f1404fb10642d2bd764e59a4b371b4c7974b932983245aea8d00c138f575ee81eb09bcb63f17b77c3270a7f5552e58f5ad78c1253d0f85962f24b5e3422ebd25ed23c646d276ee48aa58c403430bd00ce779c248ea38e8e56904f3a90f1e216c240e2ef809243fa38f299a14724c86590308ba9f8f315648c97aa1e9c236214c0078ec4d1f3c4dc728273be3407acbe61c39fe926d3fc1dd3d11dbe0164c0174727d52f7d41a1c3f9c5e2b9cb72ace03c15536579620d9a90217545a127be15151296aa67309cb62da67e67155067966fa4b138d466157d19a76b9b70910a0c1dd07d1a5637e2b9fc1471f146d6409f56f3802ae5324842c214922781c6956fb320904b01e750babb05858780d7c2b5a934f7cd2a7525a55f3169767210e8651c87a30b977728811277710b9024e30600031d7d45101749fab018d29a049c94c84004fa0b23ab686e476ae4f1b608910542c40092a40904fbc2f3b404246674f6e6934f31177acfc69132f48614e765c27122787e26b3d912740084bc112e1869c2a80b72f14416ce14a84159521c4c29a1c5b51883ddb0a364e3aea9621c7642d0f14490b1a8fd5a75cbb05d06a3514ec084df1754fe8a9996c23e72276a3ad285907fdda2624fb4e087cd5597595c1724c67d7493d32d7de75b8127c53b625b74b467883384de1b46c3b538b3f4877fc4b76ec791236cb2b21da7aed040953b24796af005399255c610a1f8e682e40a12dcfec5054b44eaa34f41f0907cf205726d31c0e54cddf0468fd53451052e38244db89c54bf09cd93c25fa651a3b3ba857bf549f5b8107890f3f406f3516150c7629a7ef04db5d4333e1f86d70a7a3720e833feb5357a20e267da0c46b1c81da2e2beb295007aaf6091768007d6ff30b076f4ffc1c5e752d67b83eea660143a820af26311bd8bdc6570a5da3236b124023a5a0bf7a4283bf7c7c7d01449f884a008d5aa3081d2b177e4fc1ab1c54d5355c80192616e07c3127503dec35e85a6015b8971133f1fbb5382cd3e93afc810e75970cec379ddd4c38512f150cce250f56baf4f329060b0306bf65714d267a5a7b4ac5f6443377fd487df636245008aa782308ec5c1ffe787bff48101442776007025c635d234dad15de989744e257c27ab187c35b34fd7139cd682929d803b530145fc44b816400446449f06e00bb0d7af4a6e54d40b55719681b22396ac4841aeef2220b9b03641f1beab80e15e73655374f1f3b02a26500e9202910b6cc253b950dd164f02ab54449d9ef09e7ee9e55e301794e56cf7a3fe3cf1c0cb0f35428974f19260d731f4788a6f1019b52914cf2e21819728ecc6edf35b122a678e031d446be7c6cd1e91a18909352fc17b05c9656f13c1822506ea253f74c5d4b5c6a40234371cc23af12ba31445280e585283f13d91355949b27b04f432877e95534c2c0322f4a225c2300e40b75bbc2096dc0e7de24ee464b31c059125879d85f5afde4f9750586c643673e8b2a8f32313026caad0338814274539258756bef776d38309218e6c1c972d466142e3db87e2a65157c4c22fd5911e126c82a698a8d22d94e6f2a5a092a4c87eda5540d118351b2f48460edf9e2184ecce97827e0fb4080ec8c554842557b085f1a4c9a3535590b003d38525282136cd1d95f3582dc45b79cee4985675e5a216b901c8d8c9c42a113d056d4273c7251d09f0775248228f08e980a73d3352b688cab5f06781754af9749337560893e43b24613e8be5f59d9ae2b49753bed0b2dd88b6e0034fc168ec8125a950a127bf7225529f8b30d4ec4369e6c59a378484e45b835af247d39da47745204b68525c6fc1414c329682dae280f5e8513562d898f9b7242f9075356cfc0787abe28700ef95e2e91f5cb3fc9331946b7591c2d23f7292c6d42ce6247f20e63071d2c6600ce5f0dcc6cc45e7fdc446179ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455247d9532d3c75661126c4317dc1527f1c938666124261a316ecaab54f58e611073b22385e24cbe50f97549a0d33d02e61b3cff7063715b006658c813657b0611781f38c344b73726cd2d6db24a9c88b302b4fe83ef5cac170c5ba1d788440c2136d706304d2e9180b2517a02fe3647377547b43695b2dc5099c1ce074693e364e84de3a75c20c4a062bd7b6426b23b0110cc4484cc5250f168f1262498896f245940da72bbc62b72086a8e12050dee530cf5b7c2835587826e8f9283b46ee175211070029b6d5ba508cade4568b2f9308718b1b47854dfb41938cf43139e1dc29c5237c5e01d9041618feb56f8ab6af72d5d8f163f47c597c1c6efa0b92a78e1724848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552819e9f75a28ed55f8a83274f25251c52a4bad73a2e058f27fab29b01a476ae43f1fcce2a70b0524fbb01da42affd9c7cd7369a491f664d1a0b1329413f143843eeed9547930f142e755076063129fe0d6032494a33dadf1e01688943015e6c637e4eb34226fd4e479a767307fb44d8025c62cc0da44b391961e4220facf63e577ce9561cd8ecca6321036949145aaf7b6e0a1f0623d9a9095fa6af50b7afd754834a29500fa1d473c21ae867fd87c31e7ab45c285ce0323b77f298778aced65111bd89739fa6c3456bd9ec7a431d436dff99e970e63e997e8f79c130da46d16ee7edf24b2412cd2fd1576b17764e065308f51604d2554b650cfb5b4e841be11e2a95993fe437d326d26dfd0602a24d3a7f8d0c2a95c2061f7ea63a7e6de5bd54c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455202e9b714c388211d6a4211534b17e23738d64a5ee8b44429fa8c8d581ecd7014edc6ad2bb372b0064bb12e4e5ff53d377a8eba5a5bcb456f45efb931db149e0a128dcb096e3ffa1a7695bb7d417f7f0d0ee24850b315aa4ffbda612afae26527ba34d24017dada304342b543c063e510adf54a595e282c0cbeff0d67feb8da2a56aca15ba4d4043132a4fb35bf4d85528cc2a418095d50636a98ce2432e1242731cf5d546a541374a5267b7781daaf29a5898973b9be7253f70df735ff0c7437ec9f2356d417dd44f05dec22b2c1d31b8f5eea390a10812352b2a9325468881863cf7064bc291756742c694b94cf56076bc3821396fb4a786f56d65ce02d78390959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f716f06ae0c14aad02fd4931b4fd6b1e03c6ab9f45c4a6bf732a4890f47dce87472da823f15f0e6ea4bcd5dd33b098931012fe3100c137aaa420ec7443dd22a392df0be9262bf490f13f120fd6b49b7616f706ffa2bd127657a9c3d634e4c546358ca55cb799376fb27d347600c782dd76e01489a6f342510664a17ee740ad90737cbd8b87b76750f459aece2144d407b1a113024104ab46600dcc05539d321b87104bfbf0d8102c94400ff8b229d3b8c44b0b5726608545f74af9249113a60bb3636bff43f9e38e8470ebf80688f918019dda84462c5e6647977a84c64d4fcd92ee7edf24b2412cd2fd1576b17764e065308f51604d2554b650cfb5b4e841be11e2a95993fe437d326d26dfd0602a24d3a7f8d0c2a95c2061f7ea63a7e6de5bd54c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552d4a0bc06d78a60428089156083532b66ccfb3805c742c30a9e62795890f86b2abda38d651197db507594b612bdccb46051202224ed148c50b6a7441d3094623d360a204a2b47d50437588f1a0a7f5342c2515441dbc8b04c9a79be29733cae083d143b6320e31e255a29216f95508376d4155d643398c6563f98e109d8ae7261e407b13e2a26df737e52a42d7689843eea3c8827339f4e3e6e295c5b98cada749b3ef45e61e1eb59871bb67c81619348c119e078f4e98455029a6334c1d024330bfd167926c21d4b04f147247995e725bc4444490ddcd932bdbd5a2c94502f39a033a070ea8710313e111a1267fa8330cf9789250096745169f0f94d3843be190959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71aaf092485751415d40b21d3ec8844906f8282600d2e1001cac52147ad565135430425c1cd6e0635d51ceba6ab05b4706f041c40939a10d3d6c37842552c31b0c008ac968d8f9474588cce139c8dfec7bb7e3b055b420ac7a52193e5f50010b0b6425205e607d287077f39b1828ace47776f15d7392b87b4b619f8d42acfb187b14d3504d2b0b0d568d1c772ceb779025d790760fd1b4fe5e2210ea13c629e3427d259b5e5e3d3a2e0d42c962b3c1976272c3ac6a41c77626052d3e7e1c4d1e18489b9231c68282621dd69768bbe6066ec4ae4842eb5e840fe80ac478e89218015af81f70b2fcc056ca1e9c64fae5b641b153c73a1ff4184fbe562c341c36e13379ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455283793e3769080a1f20d23a5e7b73a35bb1e237467fe6df734db57d3ba029bc5cf37f0b046149514a5100e133fc21bd5ce9e0d54dda31e928f0dd4734b79746244920f175b175031a58ee6b108530677222cf0910bc53e41187297966b5ec401a71e03f4313b346094d3372102081da04ac631b6a6c4a5128e7ba8d23ecc63a71240c4a04dacee34b8a0ea4092e7ba25feb64594b5a124f3bd042d85d6ce9bf0fda599e517d0b1a3b2a96545199300823e849193abcdfc253e9f963557157c42b97989111a9c5136d11ca920647d76a05d942df7eac77a868a036bc6f9ac6ca63b7591c2d23f7292c6d42ce6247f20e63071d2c6600ce5f0dcc6cc45e7fdc446179ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552d04aac32571d7b2ea22d5a7aaf33a70dc1c14d557b3d714ec946a212eb9c48647ea2f142672d871bf30120225505204700ef0225a1d4d17a80949d5c3e70e046d43379408dab55758a8d773a5501d752494fe639ecb5f71e320901000a613b19a8786742f81a803e6936b54eba23e65b45b0d37eb78c0c746488ad11ac89147d2ecd68418bf8d50188d8b53d0add7310d5d85e13d8cae770c366df0cfee11d3c09ba53057934754f6f98ae65463caf4c7ab253524fe7cb06f5029869ff923e41bbe0396df87bac6e511eff2045cbcd0ce63d216a50c8fc507ce2fa311ce4400e059d4f3ecca80f0a8762aa5093bf85795ea52263d2fe031db17f053f82f62d7262a0f00912b8a75732cc241b9c82b167fb2b2531111f897d001783078b20e77d2d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f715e025417572ab11f786f3d3d7def7649f1c78a3d025ae135e2db174c1466ef03443e550c8a62d934dac1cc58148b38219566386b87662b64b931bb42ee63b221735f31585d82236c847aef6fe4081a4532e095615518726c2add860a8ce0c9587a9f2232509492516e62866a9af85132ca38bf5e9425b92312247f1fab3ccc0f27e0c4766333b56e18e220753b5d73333ad3ee543d8ba65e38afbc263407e30b0f42a266f6a42f4d51ea7809a0ac6c053f3d5e1a434197072d90fa437d03312feed7063549881402555af9578ba200441dc70b567826bd2b0095e471beef0140edd6b62f26d7ba449f5eb17b92d6a1739c430c15a343f80db6cba35f3770d106713b6640e56f9d3f42021110626cda0d18b99c2e71e64960cd8daf0882280675c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71c5dfdf5d2ecc032d47ca4268b05c7562b4e3c85dbdac30223f6b063fb6d40f3d64656d21cb2b4e5d2236763643ab485c68fedd1074189b59d92223596a7ec26853ad0c1a67ed0d621250766d4c3cf31a8411ca32662d7140fc5ef73f0aa2877b708ba519c24f910d597c0d19cd001735a9376c0b27e15e4400280462e23ff93be120c3222b242863631f8e564fe9eb4883e00e78557a02759b6d541b11a6cc44abbbee66ca391e0b0d10a85ff461046574de896046905a685f90b51a8879af7ec35b29743c302a6df8cccc650a0925144b305151fd55a34de4d3e438ace77e7ed3fc8a4c54306e71987fcc114a9c2d6b83ffc3316e7afc5c11e4473c5726ce00aefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f712b603e4d66f1f74efd5f9c7c867c6776b3d1305ce1379c30c94be5148dbbd91d6989fa24a3c1a41c318bd0710d6d4809b398cd532fbd19610b5dca557431735bd38d9c709104b16e380cd15e36fa450ae50b8757461ef350c3fc014e8db27f409312e121fe70ff474d4f4d03ba574a46c8386612b52c392346052c4e3b4dac4b644a820fc39ce54971381d52b13b082cff369e284a97b404eec352743cb8083c3b886e2a3da06849ae2f1a3c10ea88764af92a0e57d73b31d67b6d291307cf3de9f6654ec983ae26575f8d0d58670a70242c8d484766ce715a9e1e19e05344396a5c372bfc35f00c1f8dbe61aa811724b4d27b0bd65ef33a9b6d2c42a4f6bc7524848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f2745186345522cbfd111ca4aa3654ea9342cd60a417d94a6fe0cb9129854d6061b4c0cefaf4ffbd92211dd046c5d1e4fe745622f2632fa21fe429690544322ef4866af6234484606094162391019b9b19225cc74512822d93c1fffa7156744c21539a7d8812b658a127a6c2e975fa027e87b4063b823d34bd158df475441097da1443dacaa6a4cd2d37df14d3f70bcbf28147cd6c12413e91d2b5f19467bc24c7c58d527d50e6d88cf7557c4fb70a00ca72ccc2dc813f89e4b6a2533a733335acd69b624824f31d05a2aaf0db200ae910c56bb32d3065517024838baf56d8b6b00083b863400c130eb47d505fe1321b3c6742bc896363c4fce16c421685cce77044f150c4e7caefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71ee15f40482fc6a64e78c724cf11dde252b3fec355720796036df53675673333456ba0c3a23624e782948f76278a8b40f4d4f667e6f1ed558d6b29c337e0ac47e8c802e75e3b3c34598f1025bac52941f041c184d28335c5ca24a124242f361332de6df28b2117c5de204eb0128956e02b6a421051538dd0ed85ed321a216dc00e6fbc744a4f85a55fce85d3cb4c2e55d3e209518f5faf213cc730c4fc733656e3f05d72778ffd72160dbf976a78ef9568aba630dd6ce9979642f600b02f4d57538680d0b5c0a1546ab44576ca5aa6c722f6d505fb0fff82e7c180358752a625734dc7b59c9fc267032ccb401dc1f344ab3eb4702437e2e70d797253223e83d031bfc5359c36f38279213fa195271a24ef08efb4b21e895134004841b6d2b2f041bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552c03c971072a07277d0807a26e77cc94cd23bf01458faad03e64c3a4418dddc1dd20498229b864c224de32421c821dd0d62cf121c7f8789236936b9587874550b0386f0323d704c5cddfd67126d353007a5a6f64dd1388238d578ac40e6d7d939821ef4539412a559f1329c59e282a2022e26ad7213ab8228ee3f46413d24b773ad67817758f3066f585eea1df4935270ab1c876f1dbb515fa458b70883cb886d7b390272b221a503a231dd75394557135d300e5f6b53992dd084c9769e863c1b11070029b6d5ba508cade4568b2f9308718b1b47854dfb41938cf43139e1dc29c5237c5e01d9041618feb56f8ab6af72d5d8f163f47c597c1c6efa0b92a78e1724848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552794f44704f8bec0cfe45d122c4e20258cd25c767d346486badcc5e2bf7837c0bda97a2465578f07de44830478577926b85fba768e5cea43b8565db43a57ff24461180e5daa5c9f6f1d11912893eb670bdd2c1558d35bab50d640ea20b6ad366371164a4273e5877ebb6e366996940e025e9b3b542cf26b143fd1895c01cd29402e9338600d8826427f450f56a568957855e52e13a78ff25cf3f9a41b4d8d4915d4e0b451ca4f453a519be758a54aee56c584de54905d2a2b77891f311e638c22eed7063549881402555af9578ba200441dc70b567826bd2b0095e471beef0140edd6b62f26d7ba449f5eb17b92d6a1739c430c15a343f80db6cba35f3770d106713b6640e56f9d3f42021110626cda0d18b99c2e71e64960cd8daf0882280675c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71a23b294b2d2231009e495c208175821a1d66752cfd05150a3167135783f66201777db67b04d6a604a59a76088e461e50cf19e8311f20763999842343dd0ef100a88bdc5bf850161b29fc2a2eb1546b5c9900f924e358ac5f3a678c287ce63a5d37268774d2511c68ac31341fcc5d4a2f328409087375fd37ffff974dfcaa641b29962e7b67c7367ce6deb243ee8c113453bfd64cc65b6e39eadf114494376b319e1a86436b911574bf40cf352dad571d10cb1d5069beb778377a5d169c98e27131d05a2aaf0db200ae910c56bb32d3065517024838baf56d8b6b00083b863400c130eb47d505fe1321b3c6742bc896363c4fce16c421685cce77044f150c4e7caefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f718fe3513737f289298067a04746d31f53ea250623e928867c1714054ca061721662f0ca7d3f33890f24b3882418867629e1125e73e371183cd9eb932deaed6a398f5008391285b56db5742a3459fac423fddc085c36de614e5395945f4fb501145101493e04c2322e7c82f11d17d48458fab4f437aa56c968c9be1327639f16016dd53500d1f54713ed5f69305401585f611e9f327a3ca16a1410d7562e169548e96a254c838122354853af4a37512c23ba7f613685c0a70f407ec17e34d5bf3c00c65506926f6d1bb0e4492073f57c3b865b802f16c62c69bd0126059a6b6b2a059d4f3ecca80f0a8762aa5093bf85795ea52263d2fe031db17f053f82f62d7262a0f00912b8a75732cc241b9c82b167fb2b2531111f897d001783078b20e77d2d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71e9d94a77b0f44d5922c1c15048cdb643355eaf13eaea07491e0b8447aaac9677f127e40b9c7ad17593da1d508c38711b56645a64d9f57555073dfa217a5d28068fe1831fb91323784e233f68e7e98772f2ca4f220be4156227c250569dfe1268d04b4d56a0482f2ca35a312346a4403297935a69c77a0475e9bdf16de8da6b307c6cf93d605ea51014096360ed9c542c2a9f7407c3a1ce5b8b4afb1dc4807a28bfd85d0c00f46261523faf027fbbbf76a718687a4e39db6ed20023374d59ae57ffaba86d9ff7457c49508f5b72b5122343a7ab44426e4e055eb5912a52747a36a033a070ea8710313e111a1267fa8330cf9789250096745169f0f94d3843be190959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71e080136a3cd3d865326fba51dd9dc919e2fe25778560d61c4dbbb809139fe416553e54494f178174c063835acfa5e45dcd8efb69d3d19107bd282c176b5e8b511c637179c8e61a3af4d7787081fe04701db7ee33f094fe0b7671382510051737225a465274e4e05df69e981a3a58942a12dff93d14d0ee6032acfa6105369a7de5857465c929af6909fa322f66410924dfba3b1f8a9e64007aed83327e3dfc6a1b69cc54c6ebae354911fa4f4d97e80194677a7bdfb9c8362f467515efd8b8251f84be1406ceb3595215057612ba396032fa325ece94af3eb25e11393a43cd204a28ec035167113b2d8efa28094c427d7737672288c87618e07aee1f68f30d34aa71f767543c5a51e09d864695cd910d28a172559491317bbb3efe3cb1f901043ea1673cf7df0511fdf6353f360a7926ee6c2b088d1e8c402293bc4d7586777234783a0f45525c24534b8c6955e96e4b8075a41285fef33498ded516d8fbc81d52421a2306649c3f5a273754651c1f5c1554be759c758858268e362bf9c53a4356aaf4579359cb001da42e139078721ce93a7d029b61d92f8812b269a1e2be73c96c912974d2681b2be0dc7916fd293f09b0f3446c363601e302540362b39e619579ec553a42544cb5c49b6898233257d5f9b71938c3ae03a6ac5b442e94194004f03d6f6bb0b30ea05aa612c4065555d345c54894f8c72949a87f7d344540131a90d53b6527c4203fbb322cf9b73b6df00f6534760bad113a7d675d5131b96491eabc04f601084e9e15c25973c87615de72013a14a5ef063e8aa8332c2a2908f3bd87237eba53371350db6d6788d9245101266ca51e9c160d5c196baa22a415c52def45c9f1ff76cfe0c22add649f3fb90bdb6522c67d1d0cd6896fa701724e9d3071511223580749c65031c83e5e2d5378187ef63d061cd7f60e604cc489244b3a2249c7611a42f59d6c73de04b116a7cae61e7703893914c64a52c3ed324b89825f42525e185973db6e2b1cf7a6704c26545f470fca297422c804d310735f9f886570bef977141023d07ac530644de5eb784d21a9f1206d64d93db987381d384a6a4b58d90f390b35a11a4892af0ca7c19d64a1cd2831429cdf0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b999b7c9b0b7d7bda57e04fb076f33f59e4c752ddbd214a7857a52ee21eb74eb89bab1ee1c6d04fa13a445e9f1c34600add1a58a0413741b7c3b565015dfb39a027ca19fa4ef51ac39b721d3ad4c352f18da43b9c134057a554413d0d4208530016d41eebe0fb1a55a84a3ce2bc4a50b1f2212c38a40738c9fde845a7b7493c259c817e34b0054d7d84a301d18e6a00a2c75575ce2156363d09f30dfa597f397b58372a4a84881b41e26e442053000a9070ac3a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2d3374e78cbdc6c5981dc7d7112452dfcd1bc2fe5ed9269ae94c8155b7f7e63b9477245fe89e60f5b714373cf3d0a711ce7597dfa5103285099817691933532138e5d4c3c5dc0297d38696cfd9a4a492d88be206e8f804bbaf9431dbbea7e296b4fa91f4f72c1108f05a5712ff9a7686eefea6b1f485276bc29902a16263d61edaa1d6cf631877a7810a64bca40977e77ce2e71fb323c24d46f23436a5a602896c7e032a0429b78d4216b326ca034519ced5f4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003248cf35f9dbf544016bff02e21b130809370e63f2bd1109736568417723ae295f43c779885db708ec0ab52b44b57b3294625b43779ec709937e1446c4a7c8350bfc5d0740663277d416d6416dc4092c8f92c56abe05041530263754f9a92d48238c6701e52efa63c31c7632ec9de73e87f40e34bc8f3d3040a9031b0eae417492e74e1555983f0b16bfe306b8ea15126a93b91d81670903c0e5416be6b7d7635632ce34b9b9473999b6dc6bd5f864686d2b332e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed88c60308f2c22a4e20fc4bde53db72bf2bd94087c04b174178d53028e9ae5d87d7031453943f1354aa8748df9cf61e349f070a3e2759355da09341460c744c7d5888366b7d7737ef4d440fa6d52a74854761769ab0de0e514aa9028f8f7217c8b25453cd0b1d6045dd7b59f553a4092dd65476c6676c4a908c314a4cfde2701308383d916afc2a47b5cb037da91518bb7ae55fd16d7632db3d695f769b5c406c257137925c3d0c687d7a6eb65ccc48ec37311d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eea5c57556330656867ac46c7d95687aced2255186d1a5199e95781d6ef8a62e6a837b258d9ab6050b0831108274f51fc6274b1731bf6a2f85ffa44d433bf9483aa7bb5df55c1b2171290d29c929fa24a639fe7a8a97e961935f9b33b92fbc104c72ec538dd9c64293ea5f28cdd3fb07012f7e2d774cb3508be19a71b308df03adc1305e60c3ec3bbcfa6b2004373b4a3d2ee021100cee2b4bc63a1e99c4887a02de5c140c364f6d0c5ff5268cba6f42d293ea6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8df3d07666fca5751d90c4a4b297e67b4804052d20a3a3d9907255f945f756069988f19bd3a540b1020f56af0fba3648eab5879fc1ae60c3c29203c7d8d375b0997f75bca766d0663f74538b44c6e0561c6860861213940bccdda13752f42075101fc35c312401473ac90560cd2890c9eae2f02a9f0046675ef3f5cd26850313a75885b09666c6998168d495d21d81ad775170d05b59e58f58f92286f07f173408eaf4c00ed196eaad98f318e6c58175e2b12050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f7cfdd4306bcd0318f70b90041fe5b016a47e96536a1552f32ce7c61ea62082b5d55cc0c77c5bd3bcbcc870300b5dc0dab6c7059baaa703196eab857c6830f33285c355f4b849b4524142c3a3b8fec26b7f46d25d6715e5b6929586e3aa0fb3276159765e090ba04d87b9803c1ab1d348e654d605748e34510ed6c36db4cd40429d55024ae82e26a08e0414df4410e1697db5d1567c3d90691b4dc4af2a15f1a11bbfd13b133a302be1ee80f48a49b0d2488b945000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059305f7bc9f099201c83423173c0c336a009bb46f4104a4668c94e2882425622ff6f1a4b5d88a40380a397681f8acc38049846771b25df54d4e73748fe46bb3ad264b574521dbc0ba1aefc751e40a864229afa6fcd761b727fc8156e0f0a6603c35bda5ee2675b60e5dfdb6efcc4504f8578a51472aa21015880a11a49464321d089e10c46048323f7ce0417ee12c845307ff1191804bd500e699247d640ab2cde830a222daa172f6c6fc00b73ddb67d370c1d2d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099e5cf602b992574f136083ad8676a2b78c6a30e9b02ca043537c9438e425d52205a3437f087cc5556b3112a3e125a08d9d4fc0e3c2d3a336a60af60ffcfc34618282000aaf13208c3eb8a3eab6a291922c5ac108e3a537740650826cf5c681db352643a7a58897ed3efde49770be2277f5681618aec7335edfd601c6fe8837b57abde0196113e004beff05d56b24411a434bb22bd38437b2691c85daaac980df2ea202b45c8267d8864ef5294029b099ec1a8640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e062d35b58895204875eef739a9dfd60fb628913e1a7383827c3e852e9354f1681c1481e16186e446768b42a5fdbe42edf406b188d31ee309f05627c19cf1a67d93aa36e9a7cd773c270023c475c1305b7c1f46ff3f97445e94eab3f6279274b6183166d294164430df3b042d19e407040f6cb21667d7d1bfcfa992245fbcd13b32ca277e5c4842681b0e0250854684c8cdf7910d257aa774eaca1403c0cf75efe52ba07f283700b07eae4324ca598697c9d4f310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef88d552207592165e6fab442b97750372ee784290a71a7b9b22412f6e805c011e254360369a131008605f23a37c9b57d0741828824299700ed3a627b464e46320749c77eb0c6e48fcde81713d43150843a39e75db89590fdd6aaa29becd5a7adde27133262a60606900027153b9b20b4c2aaf747fdfdd29b793904ae6b3025967372b051da7d93560e0417371f347684bd4250698fa292c4c3cd74976b2ee2214074965aff90754ba69513d5a4ede7dd3180c3b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073245e1a2ac7a2558656844660709c02ee57cf16b40d08573f56416cf126b30b281cf02775b8322601043848df0dce59de5e0e3f70479776b01ccf0c36a2db2bcdd91b4fd7926722275e153147d0476b3afe9c577a034a7bf497481b96ea1a5397897c75d6c5e644a84ed6333ec67e6960d97307d68d407ca950a208da1acf4f3834b818a630b02ae3196a4959b799035867686cc5f9652fc925a6667d087d77a2a24d38b90cfb650cc1c05c8f4e1e7d201136470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf27fa480d40274e13d8bb7a524776090ceeb96639897e71a4cf3701af8f597e46a3cf6a4788cd0e06acc675f277c221ff1d404e46dbd646617d8007adec6f12d3edef2a358c165afb6197753153ab44100b88053540ec1cb1133a0f8ff2c56e5f96545c7ae7854a7da03b04c4506b2072cf71118e08813247ec6253a446802401c8304f4ec10945659cc211f1f170296a260d09f098613c4e227247090c9751a36ebe2adea1d44a72bd2d5c50291c70d288426e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db5e203702a51c6a7ea9f30e9af1c32f45fa9d653f96a036be7acf1154473c285f4f6251ea10f013cc13c67b684ef91dc1defb29c9de393c439703540a8e6f5dc547d409ec92a755214d9e41d15d3c4d0b97155dbd4f122b1e47573d3b412f25cf1a773d077f732f4da01a47962e8a0359cb62384d3e2e6236ba803d3d305127a227237955ed4a38b167fa7ee8c07441b0dbf965f6743560e678d859a9bb2176bcfe1a7ab974163bd2c58c7bb7da184d1203a94100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007baf632d72ebee2ce5dcfe269b8b6849bad77e7e42b91865bcc5882561776d4e786001472944d4504be7c94abfe7c8078ddb7103128f3611698cc1033b4e260ef9308059b5bf2b36dcdaa92d3d77442f21c8fd20368a6a15dfb82324887f8b60d6f2556c85e816744fb4d40412db7740ca88fc2a2d52de290e1829514b702a4c1e9e98416c4802242101e8070e527f4a1780d11f8394ca1151f65b229c780618b2d86c7c73f3cb20c4e770087be5b51b0a2d4a0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000825e5454e641c0136f7bf65ff90aa221d0761f7d6d0409071bd721424c109425921f4168495ad127b4c9507e832ac13161e6240e49d79e0b29316571dc310a6fbcb7413acee1266c334e6c352025cc72df499c0381616956df9b926a50df0f26aa58360c0e4083462a46be2ecc236563c5b94e74e178a836c5ee293eb5a91c0a3e3d311cd27cf1715aa87d0b262d7b742d8ea25a78e4fb24960a4d050dd82b7773eaef42f403647b55841c5e6de0731b6211cb4e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007baf632d72ebee2ce5dcfe269b8b6849bad77e7e42b91865bcc5882561776d4e786001472944d4504be7c94abfe7c8078ddb7103128f3611698cc1033b4e260ef9308059b5bf2b36dcdaa92d3d77442f21c8fd20368a6a15dfb82324887f8b60d6f2556c85e816744fb4d40412db7740ca88fc2a2d52de290e1829514b702a4c1e9e98416c4802242101e8070e527f4a1780d11f8394ca1151f65b229c780618b2d86c7c73f3cb20c4e770087be5b51b0a2d4a0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d9e990018f5db02e0e51ae320774f07e622b07391bb0383744b9340763f1ea551ce54542ab070d743e420713e8d61079c8a90659470fc0197119175f34a140179118dd7896d226451c875d012d7dc57cad8ebd48ff21ac2664509c05b1a9005da091d066bfa552508238372d37a36a669b1e8b5ae92a3930d14f717df14e1801f6b0915867b3bc2f7e731c2e97a3f6590ea1663f034daa1bf305a30c9d6e3954630acc6a5064bb13b98289464b5aae772171187d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed72410304b1b1556751eb2a8faa6b708c5e797eed380316a3a89b0d400f00318bf0a3370a732326917a3b73f57ef40f623fc2701ac4232b9c6f3b3ea2a9fd079f7c237b4c8239767a4a0d62bba6147d90698d120b7631328dca38288f4fd901fcac9234563f02206725713cf6f6ea29b2a9cf502c030a533234047cd2e15e796e89770a06bce14f7aee811a0f89560307569b36124c5c3eb0b6de01a4dd520e91eaa81aedf7b925e5a462557da4b017fb67190b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051dea45393a63b4558b3bb77d8f4ee6e0b2def6d8f13fa7aaf5c7718dea0cf6456abec5ec3e4d426b349ac5a5353f74512207e29605c8327068f66798199e619f31882510b4835206b374833bdf380085c5cef2afd5c0a1976613c0e96c35b564691c74dd38ec85ebfbbad2ef30dd113569d837cc4b3cd0d1c2970640f85c257a79d8d2049c005387438c651a61b3a1b5ba826128c3ec670ef16f16c29178a751fa161542267cf5cc74a4e03deab4a3340a182400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fd99965b2fe95668cb9dc57bdd5eef3a644a7a20f6fc19339a1af80b50d43c4f60da84021c2fbe164b0db423eab2b97226df605bc1fe671ca734575abbf3a5093742046a136a5f013c618d66225c6c2af7a66d4dec2e8b50fda12b26e889313fb24bc17023db423f3e92ae3205fff971037a7d7c787c1f39bb57855aaa0240513bda284a1698985dc11797099e66583240386439b31a48498730e553c260736f825eda333e01a51771aeb1435992bf5ac429d63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023be082739b9d37c3ea828714aaef534fc54857a4fed27244235d2602beee31eb3da8e1cd6c5817ec19c9914691455300384c974bcc04d6aa8e22b1fce194741dae05c4bc7de1424f81f104be615c164ce7bfd0d8a713f0ece787730b9cd7c1f2182a66060e3915bd0ccb930b800765894bdb5639bc6f829932759548285407d5e888329a28f201428dbb07cfb5da4774b220a7e35a1c753b90005592be505015367a66415a27a0f0b9f9461207c2b5bf555c4370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2470f163edadb44a5794248c676582f623c861379d42d133230896aa9a7df3389318e6226abb550421a532c53b9437c24db7870826ec43fb4e03c30b04ce20adfa54940bd78ce403a15be267daebd527081794f096f8409ce4d6e099a2516467d1f70461dcc6e0e8ac2563d203c9a471723804641d9d571ef44133a26709245c7518b1c226af41b20e58529671cec0c6ad93c25ef086a1c9f0fea7a0416851f2ef8c94d7fbf6704534aea512223e90ba66f2b3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007cd440676814d03b8aa1be0d5871c579684a6268062b01563a827d24c779cc051cf5457ef9cc9063c7312e2a09463313d61c820e51f334258958da5dbb161e2b189bff22e1e2384f92304e3d7d57d86134706f2a8862c122a98bf90aafef3e79ecd79e2373b37e6c407b4f2bffa1f62710a0fa472a30126f10bb58143840b441ee0ec9183872ac4b2e5fff55d333397784c35956e0807e6a1987880d5f05856a1182c16f5305792153bed2637988007057dec0230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000693cf00cef798b0074fcb041ac2beb4a2da0c52a8e9edd6f2c4d4e0165b50d188d7f48617d02fb088660102bb9d9413b232a8102a0f73959434729079ab602228fad5f37da8f3c563732c235e401f97992c4aa2e01115b10a026fa7b3254265cf1e2df5ff22b200a59d50f51f26a1c146ed0c5541bcd094b3d415a552d70ff2836a766664376645d3c6e9265710f8c6b420d4d64b9b018621240e77815368f519005ce3307221e4540f2cc375f75a820fccde97200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd7b5729bcb077b0f8b926df22ba83ecdbf59138423a1055e448a7bfac62a1248e79054f3cf8739607da538bfc0490900c0005bec9b2d13b718ad006b7bb64f95b8853368b3055064341235e7c5675b7e1b9451d19df22db954464e318c3a115389006d11eab4006ad9fe6764c7b4547f4f121fe0d5962cc918663c51d7c5321382af59f7132a7161fb2a5cea8f856505a32776a08e36224ddcf60c0120f751cd98960ce27b01716609be4f1d39da20078aa7290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a442003fa691b13fa045874c98d9ef19df88a21329083778e66de5051e7b9248724c272fadc9067c69ca3010381c0d64f9b526425860705d451f9366d2365642cf99851999fe774b1230dc30d9036f4384a14f4c7570dd329bd9984fafd07d445a583a3befe25a1fd0bf2e607ea0a27da8d218597746117e0116030d7df8ff54dc17ea4ca44ccf5f8a94eb65ec29363e63ad187b3f068f1583132a6a6424483eef5a942684e010069466697bcc9ce35dd6960a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4b4644f05e61312637b8a1760df4e4aa431b624c8bf9d6d2c5139311049c37e3033ad79e6a58d123470fc386b061a52ddb33654f8ede406342b87384553b64987ee5b4ad441193826749c0952f9e73e6e4b766323feba1d9b3dc54484912139e3c2d03e2474cd2b08502b5029cc4b13b3061a06365cdd255b5ea02667886917296b8b42c456541310fdad04c7c9112f7993d40785cd93601d17cd10a705d805a922ef110b1aae677f7e956e9609971f05cb7136000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000072fbd50c5544c813e16a500baa049b1f4be3f5110da420355e19975675ce832456658b1fcacaf867b13dd9736128ba5b4032277514b6c553c24f727730ce47542b762919823b2079be9ef843d1ec8e2024f9800f20f6eb388e22b850b3004067c5629c4a80e15074d3ee2e2ddca11a501f88ba4d4e0926136ec5a24ca613c5355e5aec001c5ea24e5f13ee2bce4fd4287675e63f625b780ee6995d5204c132068f16714cb47f814f47778310bb6a8c7566ccc5430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000807edc1bd350ea53b198af14ddf6fd505900e368ffe6f434491965480276f1202894ab6f72a1d43d86df0d1aa7e8b70cde6fd660c144887eb963cd4dcf1faa3957c84250b27fc35354f2f40ef6afd750e1d1656be91362622d2b28019395055cbc52d177ae8ee0395417880f0fb7d46bf372933063bbd660077f057cc6024f2a7382790b568da43c6f265337331adf61f6a20150b07c964e4d7e6f25d69b1f594a6d2e661296217ede83ce4bb5b5df766d1457480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eed7bd142fff4c2439b95f587088325b9d54622fa157f372b117fa6c86555f4847083506ce1ed11153d7950eac2f1b733cc9b0354c802774b235c0050b96602789112677b35c5e763f0fbe34bd35b103b7ef321b59e5c914f94cc073dad48d79bb49f92d07bfb35532c7463f0e85ca34c3854a660c2fde18a661f434668baf7226ce2c34809c6f2053be98720c1b3a2ea807711f454fa5081ac19c20690b12285836985ae907633e77bd0a07c367224dc0301026000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012ad9860e367bb52b2f13b3459e61e756b6cc127797bbb5c13647302f135710acf3ef022dffdba47915b9160d365dc55c9cffe69e03e8819dc18ae1853b635710d1d6e47a8617664639fdd13724e203ba65cf222182d452b1ac56e11206d9527aca3ff2544068a7df1f7075aff48fc2f68f1a14a48ffa3783fdec60c38438e1963c7a802c96f81677af95b4ef9a34736f56c071717640b09513c4b2ad0550a0d8a6ab5601f7149258a4803589c30e705e6ee833e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b40414243bd886d6d966c780fce852b85d2c560daf0b22c65e3f855b776847daff2ca6c45f87a131761f15eeaf46771cbbb63737ba14251057360438e75441a8c947f2d14b0152c73799571995e7e04e0ca4d0641afa159a93e7a2eae42f87c99e3e4572fcd54441d04c1731776206e7e2c7063be610f04c08ffc6a5806a70892d0a339de2817371c52224693a8316d33e1bb6d58ebf118c60b65449c7d100d3f87004c70a19209a1abc245da73821c61afed2400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c13f45afd49e60d62ef54610d05c728b9121d4ba713a35ee842e7230df6815ec32991796af23c5c95e6a6344932804a65a51e2f31cfeb6fe6394a5e303a95259a47bc4d3ffac154714f0932feb12a7b1f0a8b248ecbbe16cd75ef06aca45028b419ec6e5a166566067d5002729e8e38ec5cfd274f4efc54231e9c6881430934cf69aa70b30815707862d664b9ab0f04dce9e209bfe4991447fecf7863c03216039dbc036e67ce0a9274771ef1c5121016b05f2600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c7c181040910233864cd12dc4da9d311960ef170919fb703b00df4643675811207e192fc2165c386f76d10be3b93804426c9929836df64b6c597d50fdeccc2fcef3337bae223f2b87306b703c3187178703716b3820375d8b566b4df6144d1c3e0689762359500204da412a22984827ffb5f84d91cf9a6182bd0b1fdad5ab436d23ef7a2b2f8f5d60edba69bb1abb41df37bf69eac5a30b12863623be0eb34fe7829e532b668f410a7c911bb44d4665d16f930a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac8c0c5050f5695318c12a19f88e5e716e69031b53cdff4c7c940b66745d5b1919f8f96c89915a5dcefb7a758e66c91868f98b7092d41818ef626b3349aa5c30ebbbac3d98d7ed7273cfa339dba8562067d98d0cb579cb61cec4b10524f45e118d37584a4ca5140ae08967679bd65a4e9c982534859c5e1e5da3b06e446f14731ee0170c43767a5d47b16a301ce6513e6512b371434c6053ed594f72f2ec24316af8bc43d9c6cb0ca57529369816e9181cb4074000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b9428541ed9fa063c7f7a689c9b2924f74072113b3b935e4b50d37d1a5f5e6f652f003a736fee1215bc353aaf535268f9f629215642734b8cf1381745ce6326608abb15dd34350315e9116439341657828dce0e6802462adc7d9610f2834c141de792783adbb0791d599a4b17b5520f0148e551e7572e3d1acf47491fd31a0b32eaa63a3c02f50a4d2c32516d9fba2d0904332154be824c834c387dddd39e55d60ef146c5e8ae405a889740d63edb23d760c82f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013a04b561737a64846edf141b5f1b20342f03b7bf9466933a786524a3e6ebc374dd7773f2cc65c79f087d66dced069343a7a3f185174fc5e3c52e579d744f44e53e831614136065a439c225eb65d051ad1331371c7f621122c70ed7439d2bb145d81c06d91636b2d71d12b158083753b5e53a443ce8ed96f8e6a0a3295e8dc78034e5851d3ea7566d4f2c61a903e21052a9c79463aabf159f4de49076a2df6213d4e7a586137255850598a4f87edeb020027214600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000819f663c67fa25bc32d613806ec15439165d51a21db392302347c6113bd917071c740386a65b710b083ce2e3428683f238a7078f26301269b339c33ba43ef19cce7fd5994b3515c8944aa1da160996a3bdf943a80818e1ff002e57ed3bae109ff397765328b5d08e0a3310f4e73bd59b54cfa55702d7b2679764e28be2b9655784b5c6fd882541827ed6404bf48ac6c05160a30a206bc00d3157d2ddf22647bdb89b702086384161eb4f0612298e03794ab095c1b671e56fcd05d48e519d36cf055122067eed80ee29d7c0c530a9d55885d140a9986fb5743c4ef73538f997a2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc067164c234492fa20558aeb327f93dd267d9c4de302c111b4655a91461ea08340c44dd586dd1b145304257b613da7ced18f551a41b57daf4630fb7902c3e92bc6510b3717d9b16890f2f438e7a60fdd75ad6be217c72309119b3363f1df86ae261b32bc45d03030e3ef66b457c6bc298149dbbb1063276a36dcf7d3b1776b1992191f6a80b86d4095f6fe5bb4fa582251cf8d59e3f2a13b57635952441beb35d7ca3e5b7197d578015a8e73650dc9e8f46a3ac747a52d0cd568e7db43e4d889c0fb304ff0e0fa2040c66b95978f1fc4a23905fd65bcab9624074d8f840e9855d57f09fa9497c10452847d70c60c6b8511e605cf810e4c95b0de6710d629e9faf2962b2e647c8609a7ea25b2d42bbfe845e8e2690799aa4ae26e512d041c1c62f00629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a0678bb0cabf170506ae8375c1a0c285a23bdf706649b287d66ad2518570ce4273de25d2444b8a307aeaa4c5eebe7b15c40cf986f7755e3772e80802310d9873d835423774340680f7ff9445ba6a0fa75f280544ed34d9868d907884c1c4fe03966a58e21e901a7378f75514e851f2d36e0ab1c622ef89473df80725cb384a36da3dde83017eae91011cc0e55ff50062ea67bf44e22d9b677b2e33d3d7ade8e03f353d93b0894f12dd1a19e31724c9f72c0af5b4f0d915841781dc57b5de37622bb7349635a371b14ba417f2157cf6370ac53293df5dc100734949376b514443cf138be53562fd302d8ee6772131b74662edf8524ab13d22a0ac71f74be0e0d62e64f47542964f55e223147076e4b9f1e9edc25331f82a21327d02e46b140b112d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc065deb8f231298fe76f84254660f6db533fc18b718d2a64610f9ce2e3c50cdf566b69e7d7b540f2708c490ea1558e5850102eb21618159d15b4724a33562bf9104dd76591829feee3e67ac30139fe86a4034849a6a8b1aa8699774c11b3a41635952a09e194125c821aa013e7806ef2274d3bb4f5e0d23740c83709256b59ce86978c6121869cc686a08bda5089c237d2fa800fc5072384b1e20512627c9a8650b3c07f133540a970156ccc5097ab17a3d5c2f9d75789d644cc667f8677ec70733ccd4bd41782e75402dd87e5ed47a142317941b0a8ccdf03724d51f1e5d9abb4009542d3f4ad01a1f00c9bc5aef928063db21ed1a119b4b38f3db68186faa564b26d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06fe603670b552b419d8de43359af61437f4770f0ea321cc389941923175d48234e17b7c73ca3a0c3280f36073ba23fa38e0ac0938b99f9c6dfedd3661316e791ae2271d241331ad0a6d9ad44c442b27119b6e95027c810d70aaddfc025f1eed7de5b07800a0029150f263d669356d8a5c2b491114f0dc6438c0623b1ef43c0b62eef57b3fa1092303d25e033cc3e0e44e76e8190ba3b9cf4c688fb75bbe13324cd0de272c5504993463eca01d9f81ee0d2e0c1860ed8c9c1a21c78232c7a5687ba26c6235ce60191df7214e2c1352285c3aa3b033e889af62c807870a08ce85656546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0677c2fa76ded7fc57c6ddaf6ae470001f391b3d4a0923bf1dc1303f61a10b9156a972850d153ba53474d7af5b65f1505b0e7b2b43c7adca2b4aa886231abfa87468b30e7aab264649c23ca54bed704c22434bfe5cbd0eb41c7052bd051fadc156a1825665c7ba7531537ba319579a9f2258527914cce6595d215c493c9c723f09efa94c01e9aee33844416d4e8a14cd66a70849195f96b20309495d010720397c6b70954f42451945fc99296fe458196faf956a1261f607587671063f7350966612839c5abc8f93040b38475e94f3683e82d95e36d60f9a2d64db6b0ac3b48820ef0f8d1851c5770e9737b061d5f672453e6e4450ab4e66624e2e1b0a2f819a19f04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc060abee13b9a328f160fca3855da276b07cc56ca19f72397453274f625c85d7974c5d4ce61be3d7e190a341c4a412f7d404d778b71ca77362e4cec145bbcbfd769ac57c9234d606d7b16448f18acb24e635a631b05be79883455af25692776843988ddc8499b348c67bb285071827d5a54df3a7f76317d67588f6aaf27c70a4645b060d9048ddb844a890f9e58952f940f5c206476d618a71b5b9dbf0410576075ac1ce438aa7f3714bef4fa23483af64a0e6f74317475d767f53a0e7696939765d9477c18e7f5b345d5879d1f6ec7c85d2d6387012b0edb64ccfc5354845ebe6bf9d49938de2a9827767ed05140f6923f3bad39598ffcc146287d3d42b8a7e153186cd66c59bd98290f92b21821adf27a56583b5f1303954e4a3be47debd2c639f667e02e2fe86716895bbd0466726620c3d6340cc221a70a7a57c90c7936557b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a685c3374d19e7837949e812231c14d69d42fd32964ce720ed6288039c318892a4781315f46966040103d615f050cd14d874ed5042c43273719e56864035eca1822365564bdfd1c6d8448094d0ab5797d30709d2ccbf9464bd5212051147dc71cb0afd02286a39107676ee853964458583a636a212b990d0b530c4a24bce2f65ba1eb4443c70a1e7a4131075e4c90c10a70337d28c98ae356911b126fea5fce537f066f7d80a46e753d3bbe0d6e7e147862953f3b7aa9bc54630fad6156d28469ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a1ee47c1f381e204cba5ed2751267823bc05df2430191832cb5173000f95f3e5dd01d0b7dc0358407ac77c079ce8dec7139a9773ce8c4a93928465d4b2df43a69ef2bf82a5f42ec794edbec3dfe83cb6249c1666e6deabe538f3f89572655f61649505138a735c2377d4b1c4600f18848311b314d60b4da712ef892663e855c5b9760aa5e230a3b753acd0073aa69ca7a8ee2c04347437345e94eaf751d6fd40dc9daa5269223736c18c9305e60212a093e280028531d5045a19d6b6d16baf638cfba0177ed183e07694c745573fdc4156662a1679b003a4c462c4f4155f4fe606546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06ee32176d31fa0e340d5b9333670b2669dbe64b225e172c471f969175fba8442c21da623a0c45c650c3fd4233fc6df04fa9d10120fdfe540ba81fb9463107e51b819af26c5ea3a960cd07a524795c51746530241c965e090591469d3f1cd7255b780afa6023e2cb4d844bd4479b0b536bcebf652559be34440d1fff39dc9cba5934bf9f65b3eb9f7bd06bbb2cb0483760c25f1d2c81e6ee492860531c95183632c799a42d89c9b14d3462e40030ea31225946b00811bd397beacef5093f483c2341fa0c1a55f4d50855163a2693b9004646caf102a0d24d005bb0f9790ed4b22caaf46f7875552d134524e27ca466305c60b7517c2cdfe04620d1bb43b3e17c6a44f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc069f7cf0555ae98d40f3fd7c059de5647d67c7250b43dc890d0aa5970a6b4c5d3a45eba31cff54f07c81e586701f347f096de4c50505383962387f512ecde0ba1bc8722640e021a554b9c06127ceea1a4fa87ece2600a66f575491b17b1f5a6830f8a8272704e27d1801c9a71c9b069e3e21f27b0984db2f088335e564a78526314ff40055c4a82d16202eea2b71cd773fd8bc58578726ff45987fab6569ae7527297d056417fbcd06768a45308781dc4dda2d5f28324308569a0ead52c5be3108dcbcfd35c1e7d15dd856be6faf0fee16a3c6b30dde2a29518f15f05a1367df3749c123383dcf5b72562c6914090d5f74b4e0501fdc43930feb9f396835c39e79a9d4ff15cef1901448cd315c1f03be4a6c7fd00d996fc4147581ac0f4952cd64a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc068b4d58542fbd24774f3caa1d9d37d3192a976361c090da4f168e831f31b6732cc0606d12b005a6116012152ae237000982c9b50a18f58a0d8971a46527535b429a58e62a2f782f3b52a2e4046b9c825f8819a5231cd889071e60f043acb36f32dc9c8c117cc38a73ab728326a97c3b37e0c78707554d845718c1d5382d9da50d225766609ad7d711c626043499772624467a7f442c0c57668e9a7d73f11eda138b8fef5ec5fd7049a57436739a3c136652a76d52d502f21da101bf3321fc1458e8e67b7b855daf23b645d374f0103213a4e4d92d4ace8c5726581f457fe651152fedbc0b5959cd3419c0620c82fb7b741f24412b41e2ea57679d5b54835fff7112999d367331350a5b0e714a3ed9f4461715fc034e5b5961b855b07bc43d5d2df467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a9b12580f8a88b03467f6ca26bb1f0a6fada691295438f51be12b5f6142cc936926787f2a499123017f5f841ab57d54263f2d767c8dc43447514b8155a4ee247afd49a41188b0e26b51f90a7250111342be00d93dfaf019615574eb2e4fc9202f2e0875120207912e83ac40210c9f11006bf4415f3e06e54fae270476bd8020513755c00322c6404a8b4bf2032774dd07b539cc27f76c7b05ed2bb471edea3505c799a42d89c9b14d3462e40030ea31225946b00811bd397beacef5093f483c2341fa0c1a55f4d50855163a2693b9004646caf102a0d24d005bb0f9790ed4b22caaf46f7875552d134524e27ca466305c60b7517c2cdfe04620d1bb43b3e17c6a44f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc060a24fe4bcc05b52ff27ba929eeff1f1e44611813a6ac9e09cd460e509a1f2b04d40997535a1de45db1b0ec36ade5040a3bcd43095d9b595cbad48011c4c0d3145940606574a6154213feb4541bd85f0111bbcc52fdc6896820fc52154c0c9b6b2dd9994ee906ce415da38c3a69b1dd0b71a0b6598bbcf92fd6221c5569fbbf6e6d845e6f72be400710038f28ecc98d31b44cad77a14d6f5a2e906b678b5522727755a7171a59822d390abb34eec9034bdf36bb6b01dc983cc0e4cf49d182c167e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06753ee510d779ba07e6cd6f04cc2b0412047a16297d24ab13f12b6c4701a982008913db3af27449056265226aa59e977af722fd4cd90b190320bf682a08b528310a26253913ae2417594645348249dd31e12d70236fbf4c63d10a196e6422b768984e8b078f6042372e274777122b3c49c5a9716a8aa5c27103ed4c2b47a0bf560c7f3813d888460b00d1e30550a754067c7a575bf174d93808d5ef56ed265317285e410baf68ad296f09c5672094c27de8acab7122bd12409b63ec4333875d16f055122067eed80ee29d7c0c530a9d55885d140a9986fb5743c4ef73538f997a2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06c33a423a9ce0417345051761e89e9f0f4790b2179847d02a33939716f059483886f83328e7347078520e535783cff601f14ad05e48af3c4dbf618f643f24ac0d0f154f01fe845d67976b76549620385e12e27c238220704ea48beb1dc1669b6faf47df14d5b23d055ccb5b43361f677eff8dbf20414ede36ef04bc575bf91d0ff82a6e579e7a8a0a81208d74a8b6461342ee6270d51e5e5ca45dd07cf1d4fb757f066f7d80a46e753d3bbe0d6e7e147862953f3b7aa9bc54630fad6156d28469ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a375e4f6350fd060330d0596fb156be4a393c3b2f43a81657ce451d32c6b8e9121fd6f215c687ae5609ab3a556ff9c12175cf36392e69352dff42d95e8a2d5f54d476525221d8e51dea932d02e67bc9733c77ab3804fc8410826edd0bbfbadf7e012142203dc2812bed084373ba027043d85bff04c42c9b10fcf76778642e5f020c7f3813d888460b00d1e30550a754067c7a575bf174d93808d5ef56ed265317285e410baf68ad296f09c5672094c27de8acab7122bd12409b63ec4333875d16f055122067eed80ee29d7c0c530a9d55885d140a9986fb5743c4ef73538f997a2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06c33a423a9ce0417345051761e89e9f0f4790b2179847d02a33939716f059483886f83328e7347078520e535783cff601f14ad05e48af3c4dbf618f643f24ac0d0f154f01fe845d67976b76549620385e12e27c238220704ea48beb1dc1669b6faf47df14d5b23d055ccb5b43361f677eff8dbf20414ede36ef04bc575bf91d0ff82a6e579e7a8a0a81208d74a8b6461342ee6270d51e5e5ca45dd07cf1d4fb757f066f7d80a46e753d3bbe0d6e7e147862953f3b7aa9bc54630fad6156d28469ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a77d1213d8a6a5e7a6a925820dc239018e97a3f57a66d1e0e0099c04a0688d443eb4d7508eb02ec1475734117a3e60b3a505fc13e3db437555e905f0500cbaa5eba56e8798b17c54aaeaa653cb13602736192f508a65e12584174431204bcf00c4055a9300e3251113e3a781934a6c36a57365b4aea58125b93aae12d402e6e19b362ed4c62d2d55a65087a6938a30e1d6aeb15253400ee60580d87560153123aff63c95fcefc7b6d2fdfcd106548897ca7cfe92a6f35807de0c6825e93612673b217b07d6c88726a6d2b5011ce7ed654bb9fe44eb081bf23d2680014e626b94faf86707d2491252189cf506f84128138c04cab5c7cf07e4b387e2d6716906f0db85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1aaed93e4a563c556c7a34fa664a27e32100a71d18610c556168fbcc767738cd0fbbbc656dae621c649fd22d5f20fd5f2529ad213abd2cf53d4c6708540982340ff32ac77e96d8cb4e525a663a303ebb4f905e6926a3cc36138058a73189f1561049505138a735c2377d4b1c4600f18848311b314d60b4da712ef892663e855c5b9760aa5e230a3b753acd0073aa69ca7a8ee2c04347437345e94eaf751d6fd40dc9daa5269223736c18c9305e60212a093e280028531d5045a19d6b6d16baf638cfba0177ed183e07694c745573fdc4156662a1679b003a4c462c4f4155f4fe606546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0615a3722766e69d15fef98c7b7bc81c294571735e612cba76d447b878c02e7e30e0c9194d60f24c6f62ac9b6ba704516efd635b224f16c2304dd61a73de3fae363e9b3a754754152ad09d1d0800015462c9e1c917dfa75656026b3452f8837d249228e3648b2e7d176592f16f6f4af725c5da13333b71b806256b85758f9098182ce03b16a895c13ed85a1c00a7e8336eac67ec67e5d2876b284ea44cddd3d864e6a04a62c99ad754964222051c1e194a527f2a617ac44151a3995d3ea7acb406f2bae72de34910694c2420197f394701bd69ef56993da716793bcd2c8ba5eb3991d1b04dc92db44fef681c2b96d97c0b58daa573af6739257ab5e04401d12b3d2aad6115f408d972a23adb2e525efb2c309af278c627235dc04a820dc40d4455f667e02e2fe86716895bbd0466726620c3d6340cc221a70a7a57c90c7936557b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a16917f16dc3dfb517bff1261ea8789192c3b1a1cb4b370628a6d7b530efb146c0bfd8906b9adfe3e8c99d343b69ef4531efe021dc2004c0dde1dd2263046554af2383743afae765a4c7ad212855a81152eba4c3bbcc7946e692a0a7b8058be4da4d72969dfb9e95932c0bc087f62920a7d05e1081eaad429c5a122513a4712279ca2ce72a04c9346896df7005e03fa006fe84d48e0694b41efb4540b1c102700f848ea05d299802c5cba0847ba8d7c3756222217f5e979157b74876e1bc8ae5e192f6102d03da03261a34300f52ebf3237090114898b41294d6e2849c0a56277c4eb1759411d4630e65b02617c77660739a0e72d579c865af7a3e731180eff1c26d56327d2cf60535e130b6d7324c1263b76dc2701f20e14e86bf91eeb478d301bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a287c027614d8f5109131965317f0b65aac30e72d1585e272277ddf2d0f226a058809712c44dffe0817800575810dba13090e523096945250c3d4ee60431193368979973fc801de38ff0e9c3528e6ad21a35ea8386930e97b4928030f151c0330b32bc45d03030e3ef66b457c6bc298149dbbb1063276a36dcf7d3b1776b1992191f6a80b86d4095f6fe5bb4fa582251cf8d59e3f2a13b57635952441beb35d7ca3e5b7197d578015a8e73650dc9e8f46a3ac747a52d0cd568e7db43e4d889c0fb304ff0e0fa2040c66b95978f1fc4a23905fd65bcab9624074d8f840e9855d57f09fa9497c10452847d70c60c6b8511e605cf810e4c95b0de6710d629e9faf2962b2e647c8609a7ea25b2d42bbfe845e8e2690799aa4ae26e512d041c1c62f00629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a204ed300dff8076667222f30adc36159ad11380e0ee226526ebab6547d817f0193fe776b6b7d1616645cdf0ff03c8d7894c24b7af15bbf2b98735d4e2986d76a3fa4b91accea351ad185f3667adf59488936993055c1750414068b6c8bc8ad195b3d257ae4afe24379977b3bfd8ddb363c16873a63d3ac6fe25bef198f00425e06e1ac03f40f30569dded62ab3c2011cfbcf743b1216a90a44842b194a59653cb413cf1ea8aa4c02516d5b44ae527d6bcc2165186be4a40fd6f8901977c1bb740dcf321aee2ad87a7bc84d4e9d25f1440ca31d38df66c32d9636f1023dee800eaf86707d2491252189cf506f84128138c04cab5c7cf07e4b387e2d6716906f0db85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1afcfa853d1757e72e943b4f5c800ecd78d4ca564550ba802626725124d6cbd96135f370183cc1d523fba433283c950836a23c4252e8c1b64b7e15a2557618ba132a140a49581a1e18d4ce3143b26b0131e071500a84219100e21a7241737d0414feaeb96afbb6c1075bf1a70a91407c11a1385b6251331459c822904b3d4cc1695bff71441a243450b069ff00a931e2613aeac26454f9ec03a5789851ef3bc53d31579e2ba7647d5b5bfe172547e47130cda4625358947e1486395915040c715f66b6c934a910901323d90e22a332d85e9c8e240390a9460a461ca146a1d3c921ae4e5201b72aeb59277da75408e41a2d0193f82b2b84a41768056b7254b2d04726d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06d9573f0ed70670165513442381566120604f2f46a4c05b16cc53f92cf2f95360594f557e360300299ce6c2019bf9a0230179a32026d74f0de0573e69277dc14ecd3a770f69e6c818b5bdc1120053531d125f6e2e4f3a6b4cad70fd46cfc957676e3ab34f02ef3a06eca1517000e77f56bd5a4b22610e7d4181fd3170a19a955b393c9e52cce1f82fdb4d392f12d9356357f6642853360f1888bfe42fbebd123f6e395a027b01d045e3eb3131b654000993840944d95adf218e687d43f9dca60e22b3db30cd1575481cc8e62a00c6b640ddc5d41c29820d4320721f1b2542db1be0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a082a7c7c09b915039cfe552bf03eaf14d67627040bf9372b2796876fbe4609123bfdc60e00e9a35ab9cf6c214391473c608d9c5676d3782e68620c57fa133348f3c4300ee62dd35ee551bf15134c0d1aa2d2231c7102126bba7bca0867f43e66073eac5f841a61391ac468307a88460e81f1043f7d44047ae2514c291121a2358a13ac31a2561436fbbb500196e1e859b1ed640aa20d6f25b8b40c407df57808768f86356a93fa05d6872d1f7a35807ed4086c681f1adb481c4888517b0e9341f6e8a050572ecf5dee146e6910cc317135cb505a560a5a510972c4326c25240509542d3f4ad01a1f00c9bc5aef928063db21ed1a119b4b38f3db68186faa564b26d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0651fced74c89e3439285cab07d475485ea355347531151329f160141196688120c1ff4064270fe87e90aa2206d095fc710db2754ea549644761871138d170597ddf3846211cac4a1af9f9772c3cb9765ca18b283c31fca338e1a6b453c1fab12eb0d08c1466c77d4d2ab671099f9b69430afc406f6b501c558f50184c2752c13428100e31008903588661a647c0d78d0f715c5f1517381f7744fc5b1d765dbc4e7755a7171a59822d390abb34eec9034bdf36bb6b01dc983cc0e4cf49d182c167e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc061dde35569eda6d39c1136123406c053294c54b077ed59417357a89028730ee29c0728457cd63f56271d07c2f79f72e60d1ec87736a489e21329460130301d054a13a754a6ac940396f6215417233c77ccac77120a574e74fbec10568361b7740e99da74e5b3da65827255d3e004b1636be78666c6006515df25d3c4a491ccc235ff37c74c641d355f12fb82d35f5bc410acce70bc3192b1d900ec335897d9436b8d2811e6147b060e1646d1794fcdc21bbf9115e89ed8856d661b06856b1ba71b320910866dfa775c43470214367aa3228bc9a566c8e9e096a4ae81fd632a61fef0f8d1851c5770e9737b061d5f672453e6e4450ab4e66624e2e1b0a2f819a19f04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc062994d9795dd2c1178eb394614b219705c655001b9d554634c8a59f36e68f9545ec21a6624c5c5757ca01b4749b4713714fc8dc4a43a8c54dc521bf62ef22255522b90f55658a2a07465b6e3272b1ed592d1b60621371d504f741076f809b105ff8f31343a69f140d5a780536505291573b7672107e6c5979af9a3b09343d5563d202785f5a294b2227b5185c70ebbf791cc54a651299b2614f32d1643f73f3282314b74395a9cc130fe8e26482fe3230fe071114f7bd351de313275ed6bbf93b67b1d3158af9ad715f6f88703e7f1a1b83c28939d0f31a158ebbcf18d8c7f51b98ce1818f6465a004714b750020b531c21f0266ff545c551be8ddf6e867da7505962da2d6a0e2f1f2de27026d16ab47a5d000f4493df0a04d0444c0bae1ee84af467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a413b547ad5d7ec1b47e261272e040c2e652bf4027e6701251167055782c7562b43465f0db7baf44fdfa0793a1759dd2888b8cd6f904d66769443a1323b3779771edb8d4f67da9831f8f9ae795bd8504def7c301c3b75725cddcad545f7511171f623a66b2c55e121b8ee834645820864db32e24d182aa203af58f82c2e73b54427766d51e2c2cb7c3259170a6dc0014414f9c2197ab8e541763bc24c2164613270d64c29ca393b590fd38c36598b8a4630498f2a9727f3708e780f63e607be2c39eb4b4ba62a565f43e7c800ef4af2616bd57c04f400412dd5213967a52645063e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06fb4d4e57806d6f551f0ada7ae076c745fb614b21fcef35247e91554b2a6ccb706156310c40cdd464dd78ba07ea5a6b1d90d06138ebd6cb48240d79398f24f6532fdc41401e1de13e37c0c21e5b9d5220f46ba441b5d74f19b0588c26c8b5ac1d0b94851e95ebd23a3387ab12c1d72e5f736b78287c459e5760770037541d91316f475e2b2bc83a7371e3f75c15668e39d2106a7026698d2fc138a703110deb6e311e9f70400f3a422889904f000e6b5fb39ed672bbbecd2c5ab0e4237d05cc17bff400499660cd54c769d04873125c758d521a19b79e1300f70c4644f600f842e5bb1756d7850f780dc75e41aaf8a82569676c52b25b7a102a6f334025d35a71bdbc02148df1791ace83fe277a36356c6571743834959d256e9ad1380bf1e5318358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06ce5d0c6371a1231de126bd26d591e3291a182073c10f83483b18be7afd77fa3e1fd62e0f46fa174f9360112c6d1acc3e9dcb4157b200de152462c55c5581d72a53567d4531e50d08bac6c7769a59d0455725ef1fd90f4e2daa9d4e0236e33b7a9dc6e4417478b4615c9d5e7eddd6067591475150803c1422bb8d092ee5eaaa5f70dc8d0512487e088319ba69ba40bb30db842f278834981c4fdb8b6d6846216b2fcd63619549613aa0576214d8767e2acc902461a7f6f472e4de865ae5ef3440e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc067f95ed2e9279523c0f7a8d7b94ec0712cd0c5369e2a55d02e7388d7d68f42f61a957b908c8491f74f7dcb735de728d3345d42917d1acc14744932027e197574b44874f2121498869189f4945bd3dbb7c37576c72762f31529577d0446031357cebb3e26989f19e3239df1b48e0da947df7241c1352373e2a7db1d44a68639a615f046b5f63c2316b39997b05f4a3141e4d88d132eb0a8a590ed63b452e34c84c1d77ee6030d64a2910503e56872fdf609cdb877799c03441b00f656a718ca860fb9ce34f090e0f65e8fa9028deceaa1b98d15a2daef5294c96a75b423970066c4fdca6594844e31048ba91122597ce5fe58169208a2a8144296fa45806f2373426d56327d2cf60535e130b6d7324c1263b76dc2701f20e14e86bf91eeb478d301bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a11e4e849c1890f079e3ce635821d421417f0323c501b196bddd80428d0c0826883b83a0df26e45280e273f3e91d7984d316e055bf0d313767a6f347c86bfd17566c59d4ebc38324e6845e62ec08caa3214ea566643569b469a215900c17ad812a287840d0e19bf4e90ebd8419e22a76203c9103d3ab29420ec1a1833c07dcf16942a852296db976a634a4678fb62e42fe81eba6eb3da373220bb2b62ce868c24d0de272c5504993463eca01d9f81ee0d2e0c1860ed8c9c1a21c78232c7a5687ba26c6235ce60191df7214e2c1352285c3aa3b033e889af62c807870a08ce85656546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc064fc3ff16d031a96c25195f5eb6fa8d18d0b2ed361687de468e59105843f5b476aa386b238a6ce86cf242164af7fbc4625141a622413ea84370c7fb57057b8d073caf241ed946d83e1830a663283c3400228b6232e72fac112134287933791a74638d441044da430f8dd3de18a467e3291648042c3012f37797589b504e1c3e3bfad8b45c54b5b66dfde8bd199452ee64194dfe4f5047b0522dd29f6d543ce7364c20e00e12a5b5039b2911701f75226cf56d16624c3f4f7766a1122fe9f01230e7107d39cacd150674e6ec4c007ec26ddb16341ad35f5f7be6cbb22fdd47d206928272200e2742713a4e6d05985f707a46e22714a210d96e21c1f65decf49719a496fb66b9604a0a73c863619930f116be73497136eede1854a72b48ee3dbc18d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06c343590e195cbd232658ee36e2b74c1db1c4d87d70bdc54a1eb7392f6150894808ad6c28d7997f457f4de258e7de8d421f00566d5e9918655f98822b30b0d877af31465f4d0bb441591796596957d555bf263608b340504b9661642e3974d733550e9d6dde254e31eb1a2119be7d89137e0c907db623050a35b7b1643e664025da68d25a920d5a1974ac861cbcb5cf7bfcd63563be4d167a5d8aa14e880be86e735b7c0bfa098a05ab85cd5f8fe965177f1836075cd0c108a198a6021a215634efeb1d5a98a11c762b0ae962cf72cf73d8d9132d56e02117defda23f6eb0321b9a86747c5602070020a605629beee921b24ee52947e9245a81a03f0f4522b05ee1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a19f1186f27d03410841e2853f4023c1bc0627221bb45a35487b69f31e866cb2c2b23433fe5f9357776f836491bb1022b6b4af848b980cb30549e290488c6ff68aed17d30164bc83a6e35061493277347d37e0a5e24f26a58b66df22ee2f22e01a051d618c004765b6d9529277ad3fa6f2458016ee7cf1272205f613254981d65a207be1cf29f1e685f36d1240b71bb6100191e5201c3126b535e5c070f7cd43d4bcee17c347d0b55ca2876313c7358363c8a005d8d3dd52748748823c009c778441ced0cff92fc63c4abc82753ab34158b5484766522da4a14014d6ad365963bd4659e568de9d42a5e4dc852f728567c9c34bc4b487d6f6d63c5b5651f79760fe1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a32a4d5617c5b0a4d8432b6253130942405d3e11a4e48235d7fa4ca10fa17e01db4527b265b48e16a94b35d1735701850ea0bc46b9c3dce3535817271b08b2c52d9482540db8d8a185b4d69387d38061213f393333fe42f59f526cc593536fe4f6ac5d162fd76f355c4195a74217b864a404af57dff1d41104b50f433023ecf58a207be1cf29f1e685f36d1240b71bb6100191e5201c3126b535e5c070f7cd43d4bcee17c347d0b55ca2876313c7358363c8a005d8d3dd52748748823c009c778441ced0cff92fc63c4abc82753ab34158b5484766522da4a14014d6ad365963bd4659e568de9d42a5e4dc852f728567c9c34bc4b487d6f6d63c5b5651f79760fe1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a2d41b667c6ff436840c2e01c4e85d8295befd11f5a4da85e58fc2a144198d17e8b799b7593fe7a5f08c6b5022a23684411307b4eaa3bc13411a96a5f8438c763592703206fcc512d6ce2383ddc8b621cd5d8210aad483f4a38534e686c3f451b3a9b0701fb24ba010d0b877aa867d47046f99d573885e051c291911ceec1a706cc30e655e65a83396507976596163503cf54990d005daf37bf5ced611f517c79032fbf40709746741df7a863b6c6431ff741321685fcc43966bde17ad4acb73c34f3d17259c12f330646590df72ceb7685eefc270cb9ef2da32c3538581aaa131cab0c030d6b71384ff8013ef74fdd63c215a20fa55e0d30a22b611c9a1c0550d0c2d168f33afa45998c821e6ff0bb3d296b8670275bd738ff26190bde63f0518392e05e05098074a3c05b324ae9b203adc91d7e3b59a62826f835254c012841024e6234121c1e46e174c132e4b56346ca7f21561327e154da4e110f7cd2606d091b083ade19362e9f724452b520e8481cab19103493f550451c4e361ef121413506ed0665686d0079ed21664427dc7064f3472215961c30aa75556601175f3327fab813306dfa2ca7acb60e23aa7a20478204698cc10a7569abe239f4076e2c3486b311fe6f3f1b5885247367b54b6c75179f7d5b17cf6ca365ca2f82962d61966ee6354c11682adef37063867587447e01c0312ca52467ff74607ce4b90334df81e05c71debc0a9ff19a4fda94af2698460960008d141abc412026e1206f552a0e956fb980996ef4bac901ea1eb34c9cc6273ec8c2de4f8dd4f92b2bb23c325db4ce0d10453234b3cbb04eb36f5d41a916411736d29b4cd1232a208afb087e976af908c1895224ee18614887e6cb490a45ba6799111e4344b2ce3aa70e410c8638cf62208c42225e74f9384f88ef0e887ddd1a14f0e877715ab61f3f293e339cf7ff79971fe15f13d6d63598d00008e583f626d4aaeb29e03cdd3731cdaf55b62a897420e06a2ae7ed714059d75609c442ab6cd9b0b915da3adb44636fd0328521985197dc6a316c8691357f651a226697d7560b4f76116516ae50a90f66676599a15522b5754c85f3b97b8594ed51c422a57ea051a6520749cd42a55ddf03086cfa65d85e3124ebe363071f3e80226d365c68b97eb327e44d0878e62cae37471279523779516531f06709f5627a20c39c6c4c49f96c235a998d42649a7f13af041c3909e6141f91508b4df31f412dd0eaff1fe927fe3edb95f32b2503067d7c777e307c65a428b67155167420bb650495e2238eef2062d3ae657824c72f3003eab02b4ab8f2228b7e7c0c80890e491cc80e0d0e13d163d898e061b0339f6594142056c4a8302d81d45c0259f2660e89598f166d9e6820d5b1ab38a39bb732c8d9cf289075ca7b9ddc4c15d196e75dcdcfd9113b8db851a5391d63daa1284f6883622cdfb1cc702017ba4361453d32511d685a816a26277ee8142467e55e140da08a762fa89e007ce73001a401b068f14c24583160a47560f247296391d038f48ff45ebf2f4704c0e50350f6c82d5bc16df30cb21dad1e58d9ee0d7603c84102fae31810696934296ed84b29a5186916ca7d5ee9328b07666255788b7af64e2d8a235628587a5a0131493ed5c9610d349940232ea04710faaecb3b5cfeb11537a9fc52f94fef64ef6da568059acf143d201828a386a72fb07ce643a0ef535fa7e9e10898a0700670f480690da44e5deb75c57e54202278548a2b4c64f24331946c7561da116d3d03e2352d58614d13062a402ae83dbb42e221fe64b1c6631657ae6f36c9fbba2a22381957b7d1ee6cb6d77637d97f8d5aa53d1b1938ae9910334c336b0db7c1231103224393b9c72a5da5df004ff9331a7946280f9aaa021f32c5ef36b4cd3075b8d7ca6c6d920e4fded3d81b1b0a2420b058b245f8cdeb0b07d0cf54bfd62e5b34f83f7a0d032e564e749f0b29e3a54ba08888585747715da822ce35172efb108fbf097aad5dcd2204be2161b6953e53251845163122122eed924604544bff789bcf0b373872e02ba40d97621dad31029195d35461548f4b2587637d2a43ed1f3401505405a8f0046ec0385ad02540626b385053756b473241c3b6599a2abf6aefe3114cf294427bc5269f286ec1fc16e04f3c38666eb63cc078f2306d6d07467e96a31e0014ea768e956f352b192e56b8a167394192a0200636d560e7e6e537501bfd4d21b2d84422353c2c10477d5e5e54b9310b4b3f0d824a9927487f355670f04f25e5a3880e2885757323aa1418e736cd71e7847c0d44847f3bdf44c63da940f009c235f613533dae4beaa92a094c9a5766847f6e433996c9313786b86f4e76d05e25e883462c9736725d96df54ec7dab540c746d246b9f2f4cf37aed17f807375e63a2b73634c8e5147622f31cfc38a51160daaa714b40cc7d88df2148a2760f742b610a0b44c2446063bee87b117158772fce524f86a2f153c32e3447d96e482891703c32396e3504d2e1780475239a22429f1614c108430d9f47320635165560f501db19d5c90a088c7f977d68ad8d556434de51ce010f4207bc3b67f8512e738272ce1ef062584852b0f241c41d616b9d6158184714156a12c1b75595a1243c2044c57797d936694f424950bf85aa6ce8ba443da301010af6a3254030d45a3cdebd805a0a2d0b59bcc75b032c53dd4ee39595246e02cc0f6da7c6256d09811d924f4b41f146116adc95d03ed2b2801c9f2c866fd465b14bbc30961990edda58c02f930e713c1b2bc46a561147c5c766ff310d0c894e375c6ab9af392fee4b28945492712175e34222adc56dd2bdbe2a455c4c5ed518dd2ce627695aedb7d767da8f183fabfa0402a7b3510c34da0a1da5f5aa60a899781090b79720f5df107264c258588b60340a85a9e53772bde66e940c224a2b037875bf565e1e9f07bb335d429f4295339220c75562580c1bff3656702b27fdf4bc0e964d616a0b3e7f1a0f3d507a77d707170fa0fc3dd02738136b0c0a108818422a13879a15413c243ea922a16de03164712e964550e1fb98398720c95fa45b2a438b04df4020581d5029d1db494d5d19731451a1471c3b4157631afa297f3b8808f25f8200c2340003767891530f916948e7ac2b22ac51d07d3eae740e4cf916401a8b352c6627fe078915b444ee1c330f8fd2096fdaf2a146fda7d36b1f0389570721d56b4b968c64cc6cd56240d1f417bd6dc9292994703dd233a55d2d2af747317bd619eb67f84fc3101815bd6fbc5c0fb3a950d6f92c3ec3b0e714a688d93fa553c976c4f8466ad12d8e35173bd12df6f1b71c1dc6cc348c783c42bdda0473bc6b051191d22a17f3a2f90637f39177029e86307f8aad4343cfa171ea16b828ff9ef11b4074b43910dca064463731153bbd8e4a07d7fa44d44a031c2e7cc82bc74c0441576ffa020f95440b7025f22fd0f4b07703dead7c5010fb6183661173a1b276375878f43893883b66d39a9d5db0d7d86fd939e0300fbee72559517c2c08117b0b4c48e657e4fb792ed7898339f294163e33ac0029ff94fa4c392df4661a020004cc0d8a6f214e075d7ecfcc3c3c829d3c99337453e07f2e2c82d02d2f7bb6372975780e5f39aaed30bbaa5f32d103da53473977115afd616d210d254e2b2e5f67236bfe184fe529071ca8be5623478b265da6e6055a212a1aaff1cd607ee2a76f745ab53efbbab071814d637dab6131408b5d845f04fbaa7e0049d74b479dce3ea2aec46100b12e1b28b9654ec2160e6276255c169fb78962830a341a3792c843c5c5e643effd151a9f271871a6a059491eafb811827ddd5bd169d344a3477810e5ba6a1e5afe7c1ded2ee93726b216065a9cd16931d50c43cb442c3d9864df41c8eb8344cde17f3a19fba745dbc8670acc547e3a153c6200251ff45e920ff577966a6f3fc334c91eb26d4b6a3dc517063b260b7e06c0e736c171746c3b26bc7200aba959736b927ae3aaf47d816bc312871ebe2e3b7c1c3af3fda408d05f755f67183d49c913fb00037afc605179147c81d350197f0d2a5fea77d209bf5ff22a7502f7447a0e7122840c5e2acd4f8301c66690369c2ac6197e5b2f0ffb225f7a9cc1b61024be610adc15400d2a758a0ab98b7962e73f1b7c433355080292306e7fc9fc4c2f5dfd6d44b8430d05e8827b9949421c9ff68519f9765834be7dfc660be9984ba7dc0767b481a627b682b02a1de2bf3537858a2f15742663221f990b3e08f81b5b21780ea670e6741e2a8d509facec2efe91d11acad11544cb517867511bea6dfd9a6712bdf1932baf19ae7758a5353cc9149254652a816e15177b7c1bc5cb53e140116c0788e10857900c2c622eb61a67b48513758a9125a915e76506d26b0545929c18415176554517691e1da9bc540c81a437c111202c65b51c1085a4d54d56e9e927cb9d083182a4d85cc99f714fa7a7237be4bef9101296e170a76ff41f8b68af3af8f7e7447c5fe3191997f104c1124379d2ad147e5ec0e679b2e2c507d5f2224d31918554c019d14765813d6381f10f48fbfa4c4273980e4cdb936b6a1251d217414bfe1f3b7a9b70082a9419964385179ebff678a314377db58b7706e5ca0d7a02f33d0a3074b90ee4d0a22b1e4c3023643adb0c150dd12727e73876ef0b3c47531d9e4afe50c569e22d810e87386268d0ef3c2c511ec56c8146ff2e0c662415d122c14f9877645a728c5d1fa5b7515b79f433288d5b1251c452b223e2dc1b569f83b24183f6b04c3280bf5664dfd96ce50db907bb290b5560c51e05a93ac447803e8f74cbd3310872ae3c34acae860b973b501a387c676e2a80fc1a1682a41d15a23a6176c7e9762170ce2f36becb36ada0b964adc8cf50696c833dde795b74a1de322fb7edc0366605f03b267f5c041f9fc53d6df7633cb6aceb5c77d83175b1714a37de977210269efd09b1af7a65b639e35b98936b3a1cf1455965a2b70e2dc5636b40fbe603276a91380576e7721ae6fe3b8bfff052b676af57a19e51399e3b120af1e15433a68fdc25bb97921bdbcc4f612f217b1cd01d2a63d05efb429a045227e51754462cf68d1553c66840f2c59549e318072eb457ca5414273d0f5333037608bb5f4ef6a338510b2c6a6d45970531b1a3536ecba612570571071167b45c205e62820e2ab7cc1c15179a147f54292c90ed811c810fb955ffec45188a21e55177b9f8455af61232711cd97b0a0b2729a8853412a69325619600b41f714b0276fd235e2ac99c841de431f626e801ce360722427d576b872ca252933c17400f6a314add2b09eaa04f53649826b788024f553db308bdb5da0ae1bd791fb2de1303db52765b20630f0847409d3af9c8403253f09a6666e426068594430cee62d35185d5d71fb52c8c3570af1e6d5af8b950c8a8414c187ef936f4a58f377d2ac56c7dd7a367c433a6063132422685571275df547c00e3d68412d67f4e678c4d3a14a346842c4ac1736d7cdc446334827f0d5b060649097bf05641ff756144f5871b6a6ed624b9d9807b00d0cd05d1525811e8bcfd64cba79b368fd4cc296e672770f7cb72083aa22525ef66c352cefad0008e699f6c6d6a477345df4a2f78b0724a366aa03871be4727a0917e1367f3420fe57f7f6ef628f00c94ab114cd9cc204b58d8ba47f3bc40219f6a34570c3b2222f6ea0425903dd00e587e7f2f8497ea544a4ab725137f536712a3cc59a8c22d795bc23f0e7beab42ab0fdf62629a2931fa477a52192bad4770b0810038241110cc22f5e6c95452c70fccaed7996939544af1af805ce4f972f0617575d9e6df34cef2b9c28fd38135d8ea903398e80e60e715bbd34a70734096998201fbd11895a48446c2be4a9dd4d15c1b2493a78ab00a4536a517def785161ebf440e7854a6df2ec4e32af6a9e3619557a664d607568a25e7d40c30d887947629f2c98d33059d8dedf490ec06c00835483127935fb0a15a49d226d1fbf3f861fdd33855d333c6ee6e21f7aea7e236714a41cef43a77be1b5746bd503d63af881017c702eb90d83ccde5bde1d53341a2b8f64ae888c5558276419759fdb6cb29eb871be620e658a9ce91594ad1d530fa9c46dd9ca0a583a2c8550aeca026468f6886386d977425bff2d16dd06c90e4909a749b3cc391790f91c5c2cf1ca381be5614f29e9fb14b2778a0e115e657e3c7540393a56285c4ad26a495dd345778705151727b9981e95191b71ae450c0f1bfb09799dba3d47860be50cc9f9383763b33557ece7ff54fb99df4569f4730b50730812ed50d616ffe7892823099c02a89a561229cdb77cb758e824ca64787832480704e78cbc2c9c1690749b019f18a64f446cf0fa27534bddb94b7dc4e925fb18f10104740a713e4d383f5b3cbc17cdebe2713c5f453fbd963223fdecdc086b48ae1b5eb001317c17860713e7bb21130c7c4c234a9244eaadfe4dca0dcd7ba3f3a150c7a8872e5a40271600aaa12356f90a28a3ec8d0d96a7a9654cdc31479ff15179b1e6330e7ff1502b3310707c36ad7046a093e249a565530112079b26dc84440297340e737299e31b050e416a97b2d260b15cef13487a646d331cdd28b66cf01004a5a56f9a400e132a2e73333674a440f83eb465c1610b7807676119ecdf074ab24b09224363f152affd2454bc5ce43c5f3c1f609bdba6620911d26bb2634b2c3932c84059df9848bac7ac1bedf6b64ca1be6122fbd16a3653088f55ddb5d6441e4c0952ce2f2d4555733c4e357ee11701cd89176c75a902219cef319c76a43efb3c1570101d664d48b1963f96d398366068a617cdce925446509c272a966b7855cb962890226658c7a16d131af56e067a4bfb04e3a3253ad14c911968f6b23b0fb92d638a1a661bc6e9964356de8b67552eb235955f376f5f61da07bdbac935f0906e7286786059e5c98a5d0099f940ef464f2a7b38b551d68133544a08cc77e0ae5a2466fda65a377bc7381e507043874694439c689f332e365708a7c5c6221a08461ba96d1315189ce759161ac66b00e4ab563108642312f440788bf24223053a742ba77759073b067253c976cd43f8cc264cf0e0fd3b3f78607d827b1f21650086085ac1571cc8d50b6145966c722e43da235e372655af2f864e1251732c16017b5088c55e1ddb870606e5176619a1f05b4a8e149b5d0158b72854ef51727f6e6c6e9039e864c08ba04d15b2171880269621ed481f070b938e48121a711e653b05607981c46e6fc39e49f197b263f6a4cc076d2e7423d540524d3b5bb675f2dc6f638805441d309f4735af92db4f04b64f26b062536923cf0a534d86a14290a30730c140eb668edacb257c569c48e88984382585f32d40a215125e1a337371e77a2fa5eb3b222fc39c574ad18b1437b0826a6d3c901a8e3cf66215982466e8944926440b043acbe89739da87421cc58e856125e23953f2e718445e1b7a039952a76689c3f6143dcc1c371fccdb5072b66b13060b2641bcb37e293cdbf00b2d5ebb4819a700779cea281f98f5a6185c71111ab55d1047ae9af90e893eb0582a785d710983b46085c3285641f754015cd5026a63845d71e9805a5259f2521b4653322aabf92912a21a194829d623271097284ef37a333098fed818be58ca40050b1f4224459001ce593954506b5b3581e0f659295b9f0ca4b87d02e03ac71f357d253d7ea48a11e625c100bed003748bd98372be263f7c9c2b5238f6f57e6b8b570e4137c0173895f7784176b67b468e7730422a99ea15a44835756ef0eb2ea4252c486bbceb7931b0cc7d21df9d366656fa30e2e08916c3d22755c62ebb0b52a3620fb2000462f159d2254f277152c5d16a13433890228ecd55405807240a9cafa969167cbb228a93060dc4cda32f79cd6f2aec22ba2a5af3fc6be4a9f03d2dc2b9292a7e8e2d307c311602a30a632aace364f1780d334bb82d385f8c9f3aca1bb21fdc8fda1185de2b251e07af79d6bace6ea015973d42ae345d93b00c268913c7291cd96a728034062a7c558f2bb80c4c1780532810cea710723f458a38bdfcbf24c7eae4311d4b3344cc39e07b425ea000bcd242239bcee96f39e7235f32181d4bdf80fc46c71ecb59e83fe15dfb6d21190cecab31cf2075239c1e1e73ee5a8362a3d33e084416c569ce33983a9128a05092d22f2ad591186ed981f3059a5ee546b69cc92c4d17fc7dba3947571b88f84393a4077bc735f952eb550756a01c4754c923a731cde0242737f9a83cf1627d7708c2c948bc7bc36f102c20066718337b2ff0a418cf10c60968140e636e33365c995d4e3693da3506ff4ae440554ac12bca869172ee2e073e3bc83a52e0795018b4ecf04b9cfdd778a4686c591ee8d3687c0037683ce3601e893aa86af4c3b036bc4bdd54ee09c850c4c0851413d8595e40fd0d44224bb9593b08762efc78351262e371719edfe0629ff86e528bb7b4032c135705dceb87083c250352b0df71580fc1731467c01771385bf637b7c25876cc3a305c3f11bb518be3e324c3242f08f7ddad4242fb900c7d36137ae35f486fab46401b40031644788f8642e492fc4d4f219b0fb857b702929682135105561baae43b4e71d619123884f95a135acb1e26dc950f93faf30f78f5ca5f2410b9266849a84b2390b7063a67ce09157413161a702138f7ef2f36df70ca4b35926f2fcf6e605170cd6e156813947c60eb0b298d7cbd4458216e0c1e631745c0625756e7b6163ee5020347f0d51a7c5eb27517fd38f544b5e1ce6ad44d4a76418ac65ac18f390cb63a29417e53db73a6de9e6d1a974272d7455646187a68072a38c22667495a04e38b2157d7fb970f1b3ca91deb7ee50ed9bce33dac77e343c0e589325de074088e15b87bbb64b23dc4ddbc2ea3e0c41045f5c2348ea7fb53e208313eff776b21ca8ace71eec36475577c1f0c37b5df78bb231d561e6fdb57b455d845a286f05aea0a2825d2cb3466ca627c34ba23cc308e17841700b27a2109401c668d2dfb09ee2eb9468f61bb2212f9aa1081003b1c1c10ad7eb0e1864b42ae64370853e66dcd702f51c5c8970ae459c173c4783433ab79353a08a2f8685ef2831c8f068d1cca80c17044c74a6d27370e0f0b6a7a27bf40fa5c1ea5554db2869f02d6e13c230e9094735b6fcb25b666b4374bed894701f45d3be8acf23dcb217b7222f1c12de3102d4e09dec37e10992c2a5b1fa451a67b685796bf7c3292f0010f26c92553cb331c2dfd4c7f62dd4b5e3da7e1c467efe2f72461519f3d2898b8481dcf5f7848098c2418fdd06e19f5561e9d53675070db713aada7d2182bd813499103af7d522620725334192afeb6714cec911e0e57c9644a771d190a5071a8476db77223b0766a2adbed2c46d6890761cf71cf0cc381421fe318cc2ea55c0e390c9e2272e3f68128e812293177a13e50ecbd4e333391c95a917d7c4f08fc6657aae96670ca90631dcc75a16932a8c039ad561d306d18b214957f5a4a724aa26828c04e37b976487d6de6b7273d3f0c74931ad277b15a425c959ed46367a09f3250d58e1db7e68b01e367d367bc106b55e80646705cebe9637d8b66485088b92a45ee6c51ac00a065f6e1a319ac0d09403282824b4cd37b697edf0d05a01c77200bd66b4c8b5b66474f5fde2c3ce598525b3fd32bb911e477b522090c626687048e2a14008e00f53ea10fe3409ad2ba11256f8d778465e738767d11636f27ca6b50de3f3329071d3a4ac6fb2c18712667b71dad57c0b9214e79d8c11d4eece20b6741e747f2f8096b92e55f0fc7508b0b8ca11657c1e99443ca3083218ac5420c0287f35464e29f4787e8d4788b501472a5ea121a0e56435161f6187cbe155f57a46df835f6aeb43fd348d54729adea7dc15bba522f90357a736b5406cc9ee94ab7b3207ea80ddf69e9dbfa11e80ec02a4a799438be7e236c7f1d5f4668219c0dd9f34937616b2e78cc5c7f2f0f4ec641fbac702850ab912100676f476680805eefa22e7dc314556e7c314a775014ed6575e7a75ca42fc64a90e6e65bd0869b367bdac754deeb7f27fac85c757fe14e7560f3c82dc26b917443a86753662b880553f7ec5d3d121a3520a6c261f714a26075c23d154145294a18344e2853cc315971625f218901fe6b94cf4c5030c3f65389fa2f2fd5c9210cd7a73e038f7ad957f9e86f5d77ad3a740cacd53353a7f5507163756a11e3cc60b3ee88592e890f5b8e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e17a9c14864912c6148c2e213a9f8343f9e29c6495bfb6b65438c2e69c0c9e9388e70c43367a58715696dc05bbeb4ec03b1d69506c6572c0f84b5f812d8d4721a7f713167b5236248adb6c332dbf0b91de9cbde77f156281e1fc53e4195754e09f544d55e3c0d914daf40f6579f4ad65ff099fb3a5fbd452f280a494d89d7c16fd4cd9870ebd2c475e4b16d2cb1617f1f0f5c23334939b17efa6f23373331ea0407e51b70d0950507cb04eb64ede2e310a105457d33e3004f3b0a3a4b6e7b7a11a682c3728eb1b841a4885d19853651657cacf95779957b3b74809967f2e925788da65c179cb0991149a40974b816725f1a3fac4e1a0d202c5dc6bf47ee813360480afb1a9327de300961bf17f59ac955100e770ef504e867d22972217b17a4418e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e6173ae22addd6556b9a12b070bfd7c51d538e906c3f33d21078a183e57e9f40b211a6c50efc0233a7c58ea47327d4f78003f9c421cc77c470bd6042f40d9a1301eb79351d3336739d9c464156b939614e117066353a9d71b1f9c8377b213135a57e65014c42a0e1a6d5f40782a0c896ad19d0d17480d54626d9ad23ce9fbf1483604de23befbfb219826bb4ccd08c85d20b0db7ec8faf4166f4f8a249779ab5ac9fe903422a5bf152500527524c3c9145638da5d45afbc14ca22cf7973eef632fe02c64900512e4a8ce1896348a1b479dbc4422574fcce5ebb7f093b8de536072588447c81ff78325410f23f53c5061de1e2584e4b1ca72e17c8be696b255d5cf30e6f6ff455bc2fd70bab00f43ca701a9a3ee1ddc0cf42f6d3af176fced481e78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a5fe8ce082041231c35d55364d4f3bf0834a5e31d2f2d973de539bd48da84c81c2d5290163d410653e63414310a4e6b15e83259361add1b537f6f6c3267bc444efc7ff5423aa4021649f38f61653f9a5c20bcc129e49568157b4c0a2f52234f6b2e64624ced676c7c685d8700db64f22facfd363dab704947735f096de78fea23fcb3d13bc824b96b13bba0553939313137ee2c4d17079036dc7f477430286d79ea878069afb7a23522265a277791fe743494102d7e1632177a16552ea7ee9378fe02c64900512e4a8ce1896348a1b479dbc4422574fcce5ebb7f093b8de536072588447c81ff78325410f23f53c5061de1e2584e4b1ca72e17c8be696b255d5cf30e6f6ff455bc2fd70bab00f43ca701a9a3ee1ddc0cf42f6d3af176fced481e78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a05e9fb7efb6afa56e8f8f1174d9972370e792f7d647fa73bfd1170166b059f19f6477b08d0ddda20249861425ac6eb1e618379372d08e13c8d76e00bd28abd45861f6d68ce59a11e078e8456b400243acad8c524bcc9542240b85b755831e05b676ed2311060825849d0e82c978a666c82fec66e7f1eae7c7603d05a23ca240985d2005f0caa583295aaa532b4b94a3f4eb3393f702ce6230bf7883790492b6d8c8b5312bbe48b79194a8d178cd62a6e4607c51f77e19b51efe92e7397a2df09ecbfb61868db5a682bb07564a4c988644d6f0110f530096c07758551311fb60ec3cb9b6cf8502c1f5f09701acb8729669abb09159dc8757504a71c6df7a1f61dc6de225be0251e349872372a71771c141028c61f2083546aeae9b453cb0d6a797b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a06ea49434eeb5957c5a8d51e4a5aa24979206517e5553b5e7d2f69775871184a4856484f289da6767d5dc028bca61c1b1a266867ff3dc145b231b100629abc3a1839755a241a8d17c03ee87650e9b133870abf74e54f221850fd6e77473fe035da00033cd8c00d29ea233c6559150d53a195b16099a9d2257a7f1619a1109a1528073a11749fc00c6c940303ac6e5c70cd2bcd74a498cd14eb146f0f0a8eba2adf7f233e9c28ed2c0690724bb0a98f26e51fef0de9748e353f9eb81138c8d14c95d36d0c0effd27b5602b627c58e3077d3b25e2dd7f72d288dd22b1898869007c125a155e43d6472f4556d1f98fee81dd07a0c0826795e5b505ca63e6abd6804cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e990e9d1b395c461c648fb32750c3932752e64369f387487135c8f761bf477868d10eb73bfdf40a0e03f60c12c5f8447cfab0b72ed844a879b077832e2ff16b77ef7a3e161b44f556de240a0b5acadd3502c84a6559bacc5a8591f352747cd7650d14ee2e723de6460b8af47367719763d1ec4772b523e5203458625aa20d136a0a4afd5d9587995322d3cf72f4669279fe787a1fc17baa39df69da46732d3a7cd14255684f9ca35c86b037649e9f6065c23763615d5f49371faa521a73324c01cb7e636d854a65426415be34ec420a62e8eba0128e4e83307fe14963978aac02f536bf104f51fb62a7b61f4524d44a202b4baf087d2f8278d4be6a4eb5672535deaedf5fb052dc43b8fec03d00b74a047edf2a2ba0acf00761882e098e16d558ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e15879241eb4aca449669864ae1cd6c5c79328b454a9e7968426e0e12e6a1573abef8e30a05507c070aecf250852eab2d23ed1b66d3821d0de265d76e30ea167badaa4302c383ad4b7314eb2305bec8094e76d84c8f632932c159c8154bea993422bc5076432d7d5e9563413c282d5a0f1a4ca01fbdd8d55b7cd7ef1bccf4a01c15fb6a37ff74b846f40f5f6bafa7f95c960085094086b050841dfd396a1c1c5cd65d6c14f90684142c5a9b6b8ebda445becf722e1714724d71c39c1f04b5da353649157dd759c71b3435513e3fac653e28c5ed4153d6b825a12e402f979b6422c3cb9b6cf8502c1f5f09701acb8729669abb09159dc8757504a71c6df7a1f61dc6de225be0251e349872372a71771c141028c61f2083546aeae9b453cb0d6a797b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a0e41665c4778d116f4040a62872b06775e7efa51a8bc75494b63af6c87cfd463139aa6198a72a076a063090cc063b074adf6e31e5dad417e62fb662d8f122e15a552793725075b1d0cb88c37d540a549ead51036e4c5cb2eae8a2900d2de5c038575b62eae92925b0ce17a2a649c651a8822c733fd4e2c1a72f709365f0f8c21fcb3d13bc824b96b13bba0553939313137ee2c4d17079036dc7f477430286d79ea878069afb7a23522265a277791fe743494102d7e1632177a16552ea7ee9378fe02c64900512e4a8ce1896348a1b479dbc4422574fcce5ebb7f093b8de536072588447c81ff78325410f23f53c5061de1e2584e4b1ca72e17c8be696b255d5cf30e6f6ff455bc2fd70bab00f43ca701a9a3ee1ddc0cf42f6d3af176fced481e78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a8fdbce1de2f79f35223ba11271191e41853e4e35d34dae027523b639186bdd534bb14e6fd26fcb2aec381b792d9f9b0d2191b332be7437701ee51768cd2ef364e1d7d262cdc9d4023820b2706a1d902b41488c799cee1311b2415138525a2e2c83187527978d631273e38427b5f2170d510a5e111e11d51bb348fa1d8e6eeb770c5e1c36c21e59467c4a2e43260129115bb2296909c05d16324d1e46caa2de671cf488265e8efa2f0cab6a183199490f27aec228b66ae0603b383828f7bb45458eb2632ccb018e37e00184630a1d1026a3584d197e7ff61558a35d1de157cd4af536bf104f51fb62a7b61f4524d44a202b4baf087d2f8278d4be6a4eb5672535deaedf5fb052dc43b8fec03d00b74a047edf2a2ba0acf00761882e098e16d558ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e5e10262566ea794a696222306a545a18f852a7163ccddc78fa4ab045d284e7638d5ccd00e93f487def22190efcb8872f6c01cd51d30ca160e95ceb289183f36faae02e26892c572843098734c461d675d9419b0999e6d97b8e3ac967e20c963ab0e64e559caba83211f8884bc10dc11b33fd53038b3421787dfb7826e30a7e3dfd17821d9df26e5f66cb3071c6b13f102a035521d989a76b396c7c6bd2b1756c0f9134178aeb9a3ebb0f857a84b24d7888d4b46f8674a812da946e68001baa6d1afcc161744dde6805547d28c7af7a3c8939c3426fa58106f0b3842954efe12a119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a161f2d562920394f5b3b7409ca85294c83559b0a08d94e2a1513dd27bf521a2b54d9985c0bb83026236cbe1e3e0cfa620bb16233f4d6f26833651f3dafa12c315445781384ef382665b1c032982dde07e54dd15f6a618f49be4fc37d28d69118cdbfd15bb5be012e93cdf04969bd3d0e21ee791282cd9d00fa77ef3faeb885495960c558ec2ab739835f8e076ce27a781e9b650fa4fed24c86fe373cc0e40f7bc4e4110a1c7ac33d7984b218b4c9bb7c91fa4c4740cdd900536f8072ac91027d8d20cf44896ef879cff74b5f96fbd74464e984795b7e120d1bf225061be9c010261f91322b0492425592045218ca963550d1e33d93398920f85e634592defb45f9e86f5d77ad3a740cacd53353a7f5507163756a11e3cc60b3ee88592e890f5b8e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e8dbc6f6bf42d8f15ca8eb874ad60e26b6934d23c9355f13d06926c18cc64d0379bb1ae1a9b93bd631fc79c3b5c1d4b235af15f633add00342eaac54c18c5b701a7473432d2322e433b25423ae283266497619703912c9548fbe06f68e9f7cb465b22195ab2e2406960c42948713e741190ed0a3d14582f1ce2148e5839364e4da33b414e1033b5260f0ed302fbadd337ccf56828469fcb5860ef132e13fc281b8dc2dc69f9beb43027421279eedf6158445ac81021fc1b0cdc6ceb456b18ab17492ab97b6fcfb81e6c88ea62a486c34897d604320e89ea2c35204e5858d587062235ad3e924d9a4b8733706a871a9b2cd4bf0831e3da073384741a14a0989316bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a257a8f469dc39a6c0d8201173a0d7f18f61e401ef3852712aac68309757af52582a9fd445133ce15b425a00fdcd3d721dc3f1f1e06bf29786d5da65ca698b07b1cb0e51ecea18879d02454382d65041f83faea2852882c4374fa085af9dff13c3e76377ab82853633d6cb4728e7cbb1387db9b67613ef26361fe4a1a5baab132d3380a112e6c4920050fea29d9820d1e7813fb4c6c144970097a9a6516059444661611459e9c9e73ffa97d33c3ed920807abf444b330424ec055d42e9818c86e23d8b84825961b027897c43ea2f394394a89043c2362c17e2b93ef18fb7c15618366b558fd3472509aec5771fae1b571b8e121280d5355090c85e11c894214668675ff1fedbf782641eae22da2f4eb630754fe0b5ad74c24f673452d2e2115777b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a2a8e3f34a6a2741c0c4c053f194edb6d6fcb854949b5766b8309490f51ab7a768c7585715a83f8654cb6a92a253a790de315730267ff8259224d431355f6332aa102ff5ea55e1d632e9d3d48f2599c4f36a51843773e0834755512077639ea2c3460676be50793586fba042f41cb030f4925d2630edc9557201f3b44bcca9d178d8fc3097a430472c905405c62bae1403d613a512bfa39413ab3d91f7f38a32d53e7b458e01a975187ce6e0bb1c47e6206c0ca5ec88503715d86a10592a0055141fa9d0675ac3f081c1cd00fc27f624daafd9064bfcedd0c4b20cf0b08d85c500a5efa6801f5163eab888b120114520dd147ed2cba630f2056c5995f1c1cfc54cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e350f2b29d90fec686f2bc56f9654d55ad4a8df01274b2e63a5bdc8644709960aaf595b513a634231ea74d441d72fd10f6f81e90761516e62f7c972128429a3022e60d16603fbae3750bcc26fa458170523007575787ac0666bb7c24434257f4339e22b294069374d3aef9852ddc07a68462e4c0f2a7e9c0187bf8a36664d40760609630ed3deb12e06c5b27c1bd3dd79d2cfd0563c6ca36970a7cd6ac7870548aa1c3f616f32fb082667872aaedcfa795842300583a0607904934e3cc86a8c211afcc161744dde6805547d28c7af7a3c8939c3426fa58106f0b3842954efe12a119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a199afc38830a9b1e06fe2364c3323125a2bf7c3a80d777061b87111d4624827ac1239d1755cf0d5e8f2d4a2060df180b38ba081f30dc284e29b2035788f6b752befceb3bd942a52ce7d5a3117f5a9f140aeb7f631efdbd210bb2263e8423076680497a5f52b8622b85a1da09dd824a7cdb60437c9572350cfa39ca0b3c27d55670fbba52f2bf4008b1235002b919863968a75b5a4e24461ca3e42134d9a8090d7ced2073dec21277a974021d4cd608020fbd5756543ca70fcd5b4c6b3c45b16181b9ca43397d8c1e5c4c1465cafa5b545a03445296934327ad87b85bd3477828119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21aabf8477c8f217024d3084741ff1ca224cab702339327c063ac9996586a71ee1d478f827af48ffa43b406d55ddd3d267a5256e60009e540508f42d40e8f7fa666579fe0090a58de5b4f90093d668f8a5b93673d4efd2dd03fb28eb812fefd2e571632b11aa5a01145d68f2c52ef364106325b4d5313a1e375897ccf2a9dc76e5d4fcf2c5e8b64e71ddfd12d7b7991351127945b04348c8f32cd933d73440e3f0153e7b458e01a975187ce6e0bb1c47e6206c0ca5ec88503715d86a10592a0055141fa9d0675ac3f081c1cd00fc27f624daafd9064bfcedd0c4b20cf0b08d85c500a5efa6801f5163eab888b120114520dd147ed2cba630f2056c5995f1c1cfc54cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e5588576061a86d65dc56ab2e21775d5f5c5d310053ebd21e6b65d527107e24558d5e3e2a44f8b82482583170b7b82866d552c221edd9771d3f6ea676cc354779ccbfa54c60c36613a3b4232e6754df757277062f9f24c36aa4b82f120901347ec8fb127e3ac43b532402154141760e52f9b2c6226a59161820a832758e54297ca64773390f010d0cf7d0126191ece242b239893af7468f679cec6e1c036603038c8b5312bbe48b79194a8d178cd62a6e4607c51f77e19b51efe92e7397a2df09ecbfb61868db5a682bb07564a4c988644d6f0110f530096c07758551311fb60ec3cb9b6cf8502c1f5f09701acb8729669abb09159dc8757504a71c6df7a1f61dc6de225be0251e349872372a71771c141028c61f2083546aeae9b453cb0d6a797b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a2b946f52edd7482402a7186aee598e0a5ef5a03115714679012d4f54ee5d6963830ddc22680703329777f14f26ca6a7cae5ebd43a885676f1ebae735f1de1f190777e33f6a6229712d486402e7b59b181f69970c7b9f4964540a274df4440c7555ea066804ac411b79a461345541fe2986512d63cf492f2dd7f03358be6f3123314c4758923d881257441d747bb4fc0a06b3982332578d5c5715bb73db713935edb98e2f4bd50e02910f68564e6bcb75c554d16db5f0053b9b88066f64bd9d4da158ff3f5c5f3353f03c4d71ce62e03baa94b97babc3313dcd0ba60c071137066d102c0d6720523a596f9e4e3bd72b335c1be1181d716825c21cac48e2abbd4aefa4dd115e6d5d36546d4671bd20df4d21e9f11d4505457afa210362cca7807419a3090bd394755658422f04002f000100000000005b7cfe5668d23301be40a376f066ba5895438370a7e5ce525cfe51712b8d251a38a18e5de26632512a11e21ff95ebf19cf9a50473327fc4b" + "data": "0x00" } } ] @@ -1303,10 +1303,10 @@ } }, "_info": { - "hash": "0xea422f16f99bc1b58fe5c1d13bc2f75c9b3b2719cdc3c40c495da9f131f2fe85", + "hash": "0x879f5976fdea34463877eebb31b5f5c7c966720d6a103273499e11d7dec42c05", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet]", - "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- 3 validators in the genesis state\n- 2 additional attestations from validators 0 and 2 (in addition to proposer)\n- Verifies that all signatures are generated correctly\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis test verifies multi-validator signature scenarios:\n- Multiple XMSS keys are generated for different validators\n- Attestations from non-proposer validators are correctly verified\n- Signature aggregation works with multiple attestations (signature positions are correct)", + "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - 3 validators in the genesis state\n - 2 additional attestations from validators 0 and 2 (in addition to proposer)\n - Verifies that all signatures are generated correctly\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n 2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This test verifies multi-validator signature scenarios:\n - Multiple XMSS keys are generated for different validators\n - Attestations from non-proposer validators are correctly verified\n - Signature aggregation works with multiple attestations (signature positions are correct)", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json index 4967d3a..2847fbe 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json @@ -1264,7 +1264,7 @@ "hash": "0xfd453261ae39cec510a775702a42457e8aefbf018d4e32cad082b20bede7a8ae", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet]", - "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis is the most basic signature generation test. It verifies:\n- XMSS key generation works\n- Signature aggregation includes proposer signature", + "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This is the most basic signature generation test. It verifies:\n - XMSS key generation works\n - Signature aggregation includes proposer signature", "fixtureFormat": "verify_signatures_test" } } From 3164044ad924d67ef00e9f2394103b59cd345c2d Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Tue, 23 Dec 2025 16:27:26 +0200 Subject: [PATCH 21/48] added tests, fixed some types --- .../unit_tests/attestation_aggregation.rs | 31 ++++++------------- .../containers/tests/unit_tests/mod.rs | 1 + 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs index 5d1e4dc..285aa46 100644 --- a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs +++ b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs @@ -1,11 +1,10 @@ +#[cfg(feature = "devnet2")] #[cfg(test)] mod tests { - use containers::attestation::{ - AggregatedAttestation, AggregationBits, Attestation, AttestationData, - }; + use containers::attestation::{AggregatedAttestation, AggregationBits, Attestation, AttestationData}; + use containers::{Bytes32, Uint64}; use containers::checkpoint::Checkpoint; use containers::slot::Slot; - use containers::{Bytes32, Uint64}; #[test] fn test_aggregated_attestation_structure() { @@ -22,22 +21,17 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - }, + } }; let bits = AggregationBits::from_validator_indices(&vec![2, 7]); let agg = AggregatedAttestation { aggregation_bits: bits.clone(), - data: att_data.clone(), + data: att_data.clone() }; let indices = agg.aggregation_bits.to_validator_indices(); - assert_eq!( - indices - .into_iter() - .collect::>(), - vec![2, 7].into_iter().collect() - ); + assert_eq!(indices.into_iter().collect::>(), vec![2, 7].into_iter().collect()); assert_eq!(agg.data, att_data); } @@ -56,7 +50,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - }, + } }; let att_data2 = AttestationData { slot: Slot(6), @@ -71,7 +65,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(3), - }, + } }; let attestations = vec![ @@ -94,12 +88,7 @@ mod tests { let agg1 = aggregated.iter().find(|agg| agg.data == att_data1).unwrap(); let validator_ids1 = agg1.aggregation_bits.to_validator_indices(); - assert_eq!( - validator_ids1 - .into_iter() - .collect::>(), - vec![1, 3].into_iter().collect() - ); + assert_eq!(validator_ids1.into_iter().collect::>(), vec![1, 3].into_iter().collect()); let agg2 = aggregated.iter().find(|agg| agg.data == att_data2).unwrap(); let validator_ids2 = agg2.aggregation_bits.to_validator_indices(); @@ -127,7 +116,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - }, + } }; let attestations = vec![Attestation { diff --git a/lean_client/containers/tests/unit_tests/mod.rs b/lean_client/containers/tests/unit_tests/mod.rs index 315792d..42747d4 100644 --- a/lean_client/containers/tests/unit_tests/mod.rs +++ b/lean_client/containers/tests/unit_tests/mod.rs @@ -10,3 +10,4 @@ mod state_justifications; mod common; mod state_process; mod state_transition; +mod attestation_aggregation; From 94c783627f41741c7008a4036e22933c19266b36 Mon Sep 17 00:00:00 2001 From: Julius Mieliauskas Date: Mon, 29 Dec 2025 12:37:57 +0200 Subject: [PATCH 22/48] fixed environment selection by adding a minimal crate `env-config`. Added readme on how to select devnet --- lean_client/ENVIRONMENT_SELECTION.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 lean_client/ENVIRONMENT_SELECTION.md diff --git a/lean_client/ENVIRONMENT_SELECTION.md b/lean_client/ENVIRONMENT_SELECTION.md new file mode 100644 index 0000000..d906c9d --- /dev/null +++ b/lean_client/ENVIRONMENT_SELECTION.md @@ -0,0 +1,26 @@ +### To select which devnet you want to compile + +#### Option A +- Change the default features in root `Cargo.toml`: +```toml +[features] +default = ["devnet1", "<...other features>"] # Change to "devnet2" if needed +devnet1 = [...] +devnet2 = [...] +``` + +#### Option B +- Use the `--no-default-features` flag and specify the desired devnet feature when building or running the project: +```bash +cargo build --no-default-features --features devnet1 # Change to devnet2 +``` + + +### Running tests for a specific devnet + +From root directory, use the following command: +```bash +cargo test -p --no-default-features --features devnet1 # Change to devnet2 +``` + +Use `` to specify the crate you want to test. \ No newline at end of file From 41eedb56ad9e0c94ccbf23bfa879ae14fb85510c Mon Sep 17 00:00:00 2001 From: Darius Spr <108625236+Dariusspr@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:34:01 +0200 Subject: [PATCH 23/48] format code --- .../unit_tests/attestation_aggregation.rs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs index 285aa46..72d48b4 100644 --- a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs +++ b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs @@ -1,10 +1,12 @@ #[cfg(feature = "devnet2")] #[cfg(test)] mod tests { - use containers::attestation::{AggregatedAttestation, AggregationBits, Attestation, AttestationData}; - use containers::{Bytes32, Uint64}; + use containers::attestation::{ + AggregatedAttestation, AggregationBits, Attestation, AttestationData, + }; use containers::checkpoint::Checkpoint; use containers::slot::Slot; + use containers::{Bytes32, Uint64}; #[test] fn test_aggregated_attestation_structure() { @@ -21,17 +23,22 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - } + }, }; let bits = AggregationBits::from_validator_indices(&vec![2, 7]); let agg = AggregatedAttestation { aggregation_bits: bits.clone(), - data: att_data.clone() + data: att_data.clone(), }; let indices = agg.aggregation_bits.to_validator_indices(); - assert_eq!(indices.into_iter().collect::>(), vec![2, 7].into_iter().collect()); + assert_eq!( + indices + .into_iter() + .collect::>(), + vec![2, 7].into_iter().collect() + ); assert_eq!(agg.data, att_data); } @@ -50,7 +57,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - } + }, }; let att_data2 = AttestationData { slot: Slot(6), @@ -65,7 +72,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(3), - } + }, }; let attestations = vec![ @@ -88,7 +95,12 @@ mod tests { let agg1 = aggregated.iter().find(|agg| agg.data == att_data1).unwrap(); let validator_ids1 = agg1.aggregation_bits.to_validator_indices(); - assert_eq!(validator_ids1.into_iter().collect::>(), vec![1, 3].into_iter().collect()); + assert_eq!( + validator_ids1 + .into_iter() + .collect::>(), + vec![1, 3].into_iter().collect() + ); let agg2 = aggregated.iter().find(|agg| agg.data == att_data2).unwrap(); let validator_ids2 = agg2.aggregation_bits.to_validator_indices(); @@ -116,7 +128,7 @@ mod tests { source: Checkpoint { root: Bytes32::default(), slot: Slot(2), - } + }, }; let attestations = vec![Attestation { From fd9a916e3244fd594016492e0e3d7eb79c2173a8 Mon Sep 17 00:00:00 2001 From: Dariusspr <108625236+Dariusspr@users.noreply.github.com> Date: Sun, 18 Jan 2026 23:34:59 +0200 Subject: [PATCH 24/48] Remove all-features flag. Cant build both dev-nets together --- lean_client/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lean_client/Makefile b/lean_client/Makefile index 6153df6..72e702f 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -42,7 +42,7 @@ check-format: .PHONY: test test: - cargo test --workspace --all-features --no-fail-fast + cargo test --workspace --no-fail-fast .PHONY: generate-test-vectors generate-test-vectors: From 5f2d5245b8f3484e7a781f6fb2754e66d6a5570a Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:47:57 +0200 Subject: [PATCH 25/48] feat: add discv5 dependencies --- lean_client/networking/Cargo.toml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lean_client/networking/Cargo.toml b/lean_client/networking/Cargo.toml index a45e3a9..b4c2b89 100644 --- a/lean_client/networking/Cargo.toml +++ b/lean_client/networking/Cargo.toml @@ -15,6 +15,9 @@ snap = {workspace = true} sha2 = { workspace = true } anyhow = { workspace = true } async-trait = "0.1" +discv5 = "0.10.2" +enr = { version = "0.13", features = ["k256"] } +k256 = "0.13" futures = "0.3" libp2p-identity = { version = "0.2", features = ["secp256k1"] } libp2p-mplex = "0.39" @@ -26,7 +29,11 @@ yamux = "0.12" ssz = { workspace = true } serde = { workspace = true } serde_yaml = { workspace = true } -discv5 = "0.10.2" hex = "0.4.3" tiny-keccak = "2.0.2" derive_more = "2.1.1" + +[dev-dependencies] +hex = "0.4" +num-bigint = "0.4" +num-traits = "0.2" From 01ac28986d81c1026b80c45c924c899d9f94dfe9 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:48:09 +0200 Subject: [PATCH 26/48] feat: add discovery config --- .../networking/src/discovery/config.rs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lean_client/networking/src/discovery/config.rs diff --git a/lean_client/networking/src/discovery/config.rs b/lean_client/networking/src/discovery/config.rs new file mode 100644 index 0000000..b613cc7 --- /dev/null +++ b/lean_client/networking/src/discovery/config.rs @@ -0,0 +1,40 @@ +use std::net::IpAddr; + +use discv5::enr::CombinedKey; +use enr::Enr; + +#[derive(Debug, Clone)] +pub struct DiscoveryConfig { + pub enabled: bool, + pub udp_port: u16, + pub libp2p_port: u16, + pub listen_address: IpAddr, + pub bootnodes: Vec>, +} + +impl DiscoveryConfig { + pub fn new(listen_address: IpAddr, udp_port: u16, libp2p_port: u16) -> Self { + Self { + enabled: true, + udp_port, + libp2p_port, + listen_address, + bootnodes: Vec::new(), + } + } + + pub fn with_bootnodes(mut self, bootnodes: Vec>) -> Self { + self.bootnodes = bootnodes; + self + } + + pub fn disabled() -> Self { + Self { + enabled: false, + udp_port: 0, + libp2p_port: 0, + listen_address: IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), + bootnodes: Vec::new(), + } + } +} From dfdff3b508ee9e88b983c6b4426e933b10057e6e Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:48:22 +0200 Subject: [PATCH 27/48] feat: add discovery service --- lean_client/networking/src/discovery/mod.rs | 219 ++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 lean_client/networking/src/discovery/mod.rs diff --git a/lean_client/networking/src/discovery/mod.rs b/lean_client/networking/src/discovery/mod.rs new file mode 100644 index 0000000..d0b67db --- /dev/null +++ b/lean_client/networking/src/discovery/mod.rs @@ -0,0 +1,219 @@ +pub mod config; + +#[cfg(test)] +mod tests; + +use std::net::IpAddr; +use std::sync::Arc; + +use anyhow::{Result, anyhow}; +use discv5::enr::{CombinedKey, NodeId}; +use discv5::{ConfigBuilder, Discv5, Event as Discv5Event, ListenConfig}; +use enr::{Builder as EnrBuilder, Enr}; +use libp2p::Multiaddr; +use libp2p::multiaddr::Protocol; +use libp2p_identity::{Keypair, PeerId}; +use tokio::sync::mpsc; +use tracing::{debug, info, warn}; + +pub use config::DiscoveryConfig; + +/// Discovery service that wraps discv5 for peer discovery. +pub struct DiscoveryService { + discv5: Arc, + local_enr: Enr, + event_receiver: mpsc::Receiver, +} + +impl DiscoveryService { + pub async fn new(config: DiscoveryConfig, keypair: &Keypair) -> Result { + let enr_key = keypair_to_enr_key(keypair)?; + + let local_enr = build_enr(&enr_key, config.listen_address, config.udp_port, config.libp2p_port)?; + + info!( + enr = %local_enr, + node_id = %local_enr.node_id(), + "Built local ENR" + ); + + let listen_config = ListenConfig::from_ip(config.listen_address, config.udp_port); + + let discv5_config = ConfigBuilder::new(listen_config).build(); + + let mut discv5 = Discv5::new(local_enr.clone(), enr_key, discv5_config) + .map_err(|e| anyhow!("Failed to create discv5: {e}"))?; + + for bootnode in &config.bootnodes { + if let Err(e) = discv5.add_enr(bootnode.clone()) { + warn!(enr = %bootnode, error = ?e, "Failed to add bootnode ENR"); + } else { + info!(enr = %bootnode, "Added bootnode ENR"); + } + } + + discv5 + .start() + .await + .map_err(|e| anyhow!("Failed to start discv5: {e}"))?; + + let event_receiver = discv5 + .event_stream() + .await + .map_err(|e| anyhow!("Failed to get discv5 event stream: {e}"))?; + + info!("Discovery service started"); + + Ok(Self { + discv5: Arc::new(discv5), + local_enr, + event_receiver, + }) + } + + pub fn local_enr(&self) -> &Enr { + &self.local_enr + } + + pub async fn recv(&mut self) -> Option> { + loop { + match self.event_receiver.recv().await { + Some(event) => { + match event { + Discv5Event::Discovered(enr) => { + info!( + node_id = %enr.node_id(), + "Discovered peer via discv5" + ); + return Some(enr); + } + Discv5Event::SocketUpdated(addr) => { + info!(?addr, "discv5 socket updated"); + } + Discv5Event::SessionEstablished(enr, addr) => { + debug!( + node_id = %enr.node_id(), + ?addr, + "discv5 session established" + ); + } + Discv5Event::TalkRequest(_) => { + // We don't handle TALKREQ for now + } + Discv5Event::NodeInserted { node_id, replaced } => { + debug!( + %node_id, + ?replaced, + "Node inserted into routing table" + ); + } + _ => { + // Handle any new event types added in future versions + } + } + } + None => return None, + } + } + } + + pub fn enr_to_multiaddr(enr: &Enr) -> Option { + let ip = enr.ip4().map(IpAddr::V4).or_else(|| enr.ip6().map(IpAddr::V6))?; + let libp2p_port = enr.tcp4().or_else(|| enr.tcp6())?; + + let peer_id = enr_to_peer_id(enr)?; + + let mut multiaddr: Multiaddr = ip.into(); + multiaddr.push(Protocol::Udp(libp2p_port)); + multiaddr.push(Protocol::QuicV1); + multiaddr.push(Protocol::P2p(peer_id)); + + Some(multiaddr) + } + + pub fn find_random_peers(&self) { + let random_node_id = generate_random_node_id(); + debug!(%random_node_id, "Starting random peer discovery lookup"); + + let discv5 = Arc::clone(&self.discv5); + tokio::spawn(async move { + match discv5.find_node(random_node_id).await { + Ok(nodes) => { + info!(count = nodes.len(), "Random lookup completed"); + } + Err(e) => { + warn!(error = ?e, "Random lookup failed"); + } + } + }); + } + + pub fn connected_peers(&self) -> usize { + self.discv5.connected_peers() + } +} + +fn keypair_to_enr_key(keypair: &Keypair) -> Result { + match keypair.key_type() { + libp2p_identity::KeyType::Secp256k1 => { + let secp_keypair = keypair + .clone() + .try_into_secp256k1() + .map_err(|_| anyhow!("Failed to convert to secp256k1"))?; + + let secret_bytes = secp_keypair.secret().to_bytes(); + let secret_key = k256::ecdsa::SigningKey::from_slice(&secret_bytes) + .map_err(|e| anyhow!("Failed to create signing key: {e}"))?; + + Ok(CombinedKey::Secp256k1(secret_key)) + } + other => Err(anyhow!("Unsupported key type for discv5: {:?}", other)), + } +} + +fn build_enr(key: &CombinedKey, ip: IpAddr, udp_port: u16, libp2p_port: u16) -> Result> { + let mut builder = EnrBuilder::default(); + + // libp2p port is stored in tcp field, since Enr doesn't have a field for a quic port + match ip { + IpAddr::V4(ipv4) => { + builder.ip4(ipv4); + builder.udp4(udp_port); + builder.tcp4(libp2p_port); + } + IpAddr::V6(ipv6) => { + builder.ip6(ipv6); + builder.udp6(udp_port); + builder.tcp6(libp2p_port); + } + } + + builder + .build(key) + .map_err(|e| anyhow!("Failed to build ENR: {e}")) +} + +fn enr_to_peer_id(enr: &Enr) -> Option { + let public_key = enr.public_key(); + + match public_key { + discv5::enr::CombinedPublicKey::Secp256k1(pk) => { + let compressed = pk.to_sec1_bytes(); + let libp2p_pk = libp2p_identity::secp256k1::PublicKey::try_from_bytes(&compressed).ok()?; + let public = libp2p_identity::PublicKey::from(libp2p_pk); + Some(PeerId::from_public_key(&public)) + } + _ => None, + } +} + +pub fn parse_enr(enr_str: &str) -> Result> { + enr_str + .parse() + .map_err(|e| anyhow!("Failed to parse ENR: {e}")) +} + +fn generate_random_node_id() -> NodeId { + let random_bytes: [u8; 32] = rand::random(); + NodeId::new(&random_bytes) +} From cbcb6b10c11e865683c9d6b72d98dcb1610135ed Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:48:34 +0200 Subject: [PATCH 28/48] feat: add discovery module export --- lean_client/networking/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lean_client/networking/src/lib.rs b/lean_client/networking/src/lib.rs index 16e0483..f382926 100644 --- a/lean_client/networking/src/lib.rs +++ b/lean_client/networking/src/lib.rs @@ -1,5 +1,6 @@ pub mod bootnodes; pub mod compressor; +pub mod discovery; mod enr_ext; pub mod gossipsub; pub mod network; From 6a1fa7658420a0c182fadb35b4864321cccaa747 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:48:45 +0200 Subject: [PATCH 29/48] feat: add ENR bootnode support --- lean_client/networking/src/bootnodes.rs | 86 +++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/lean_client/networking/src/bootnodes.rs b/lean_client/networking/src/bootnodes.rs index 264ec02..427f4ae 100644 --- a/lean_client/networking/src/bootnodes.rs +++ b/lean_client/networking/src/bootnodes.rs @@ -1,6 +1,11 @@ use std::sync::Arc; +use discv5::enr::CombinedKey; +use enr::Enr; use libp2p::Multiaddr; +use tracing::warn; + +use crate::discovery::{DiscoveryService, parse_enr}; pub trait BootnodeSource: Send + Sync { fn to_multiaddrs(&self) -> Vec; @@ -24,17 +29,90 @@ impl BootnodeSource for Arc<[Multiaddr]> { } } +#[derive(Debug, Clone)] +pub enum Bootnode { + Multiaddr(Multiaddr), + Enr(Enr), +} + +impl Bootnode { + pub fn parse(s: &str) -> Option { + if s.starts_with("enr:") { + match parse_enr(s) { + Ok(enr) => Some(Bootnode::Enr(enr)), + Err(e) => { + warn!(bootnode = s, error = ?e, "Failed to parse ENR bootnode"); + None + } + } + } else { + match s.parse::() { + Ok(addr) => Some(Bootnode::Multiaddr(addr)), + Err(e) => { + warn!(bootnode = s, error = ?e, "Failed to parse Multiaddr bootnode"); + None + } + } + } + } + + pub fn to_multiaddr(&self) -> Option { + match self { + Bootnode::Multiaddr(addr) => Some(addr.clone()), + Bootnode::Enr(enr) => DiscoveryService::enr_to_multiaddr(enr), + } + } + + pub fn as_enr(&self) -> Option<&Enr> { + match self { + Bootnode::Enr(enr) => Some(enr), + Bootnode::Multiaddr(_) => None, + } + } +} + #[derive(Debug, Clone, Default)] -pub struct StaticBootnodes(Vec); +pub struct StaticBootnodes { + multiaddrs: Vec, + enrs: Vec>, +} impl StaticBootnodes { - pub fn new>>(addrs: T) -> Self { - StaticBootnodes(addrs.into()) + pub fn new(bootnodes: Vec) -> Self { + let mut multiaddrs = Vec::new(); + let mut enrs = Vec::new(); + + for bootnode in bootnodes { + match bootnode { + Bootnode::Multiaddr(addr) => multiaddrs.push(addr), + Bootnode::Enr(enr) => { + // Convert ENR to multiaddr for libp2p connection + if let Some(addr) = DiscoveryService::enr_to_multiaddr(&enr) { + multiaddrs.push(addr); + } + enrs.push(enr); + } + } + } + + StaticBootnodes { multiaddrs, enrs } + } + + pub fn parse(bootnode_strs: &[String]) -> Self { + let bootnodes: Vec = bootnode_strs + .iter() + .filter_map(|s| Bootnode::parse(s)) + .collect(); + Self::new(bootnodes) + } + + pub fn enrs(&self) -> &[Enr] { + &self.enrs } } impl BootnodeSource for StaticBootnodes { fn to_multiaddrs(&self) -> Vec { - self.0.clone() + self.multiaddrs.clone() } } From 594b4494fc84992e482bde83e5d21b8248994843 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:48:55 +0200 Subject: [PATCH 30/48] feat: integrate discovery into network service --- lean_client/networking/src/network/service.rs | 142 +++++++++--------- 1 file changed, 75 insertions(+), 67 deletions(-) diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index 47eaa9f..23d248a 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -1,6 +1,5 @@ use std::{ collections::HashMap, - fs::File, net::IpAddr, num::{NonZeroU8, NonZeroUsize}, sync::Arc, @@ -9,8 +8,6 @@ use std::{ use anyhow::{Result, anyhow}; use containers::ssz::SszWrite; -use derive_more::Display; -use discv5::Enr; use futures::StreamExt; use libp2p::{ Multiaddr, SwarmBuilder, @@ -22,7 +19,6 @@ use libp2p::{ }; use libp2p_identity::{Keypair, PeerId}; use parking_lot::Mutex; -use serde::{Deserialize, Serialize}; use tokio::select; use tokio::time::{Duration, MissedTickBehavior, interval}; use tracing::{debug, info, trace, warn}; @@ -30,6 +26,7 @@ use tracing::{debug, info, trace, warn}; use crate::{ bootnodes::{BootnodeSource, StaticBootnodes}, compressor::Compressor, + discovery::{DiscoveryConfig, DiscoveryService}, enr_ext::EnrExt, gossipsub::{self, config::GossipsubConfig, message::GossipsubMessage, topic::GossipsubKind}, network::behaviour::{LeanNetworkBehaviour, LeanNetworkBehaviourEvent}, @@ -44,87 +41,36 @@ pub struct NetworkServiceConfig { pub gossipsub_config: GossipsubConfig, pub socket_address: IpAddr, pub socket_port: u16, + pub discovery_port: u16, + pub discovery_enabled: bool, bootnodes: StaticBootnodes, } -#[derive(Debug, Clone, Serialize, Deserialize, Display)] -#[serde(untagged)] -enum Bootnode { - Multiaddr(Multiaddr), - Enr(Enr), -} - -impl Bootnode { - fn addrs(&self) -> Vec { - match self { - Self::Multiaddr(addr) => vec![addr.clone()], - Self::Enr(enr) => enr.multiaddr_quic(), - } - } -} - -fn parse_bootnode_argument(arg: &str) -> Vec { - if let Some(value) = arg.parse::().ok() { - return vec![Bootnode::Multiaddr(value)]; - }; - - if let Some(rec) = arg.parse::().ok() { - return vec![Bootnode::Enr(rec)]; - } - - let Some(file) = File::open(&arg).ok() else { - warn!( - "value {arg:?} provided as bootnode is not recognized - it is not valid multiaddr nor valid path to file containing bootnodes." - ); - - return Vec::new(); - }; - - let bootnodes: Vec = match serde_yaml::from_reader(file) { - Ok(value) => value, - Err(err) => { - warn!("failed to read bootnodes from {arg:?}: {err:?}"); - - return Vec::new(); - } - }; - - if bootnodes.is_empty() { - warn!("provided file with bootnodes {arg:?} is empty"); - } - - bootnodes -} - impl NetworkServiceConfig { pub fn new( gossipsub_config: GossipsubConfig, socket_address: IpAddr, socket_port: u16, + discovery_port: u16, + discovery_enabled: bool, bootnodes: Vec, ) -> Self { - let bootnodes = StaticBootnodes::new( - bootnodes - .iter() - .flat_map(|addr_str| parse_bootnode_argument(&addr_str)) - .flat_map(|bootnode| { - let addrs = bootnode.addrs(); - if addrs.is_empty() { - warn!("bootnode {bootnode} doesn't have valid address to dial"); - } - - addrs - }) - .collect::>(), - ); + let bootnodes = StaticBootnodes::parse(&bootnodes); NetworkServiceConfig { gossipsub_config, socket_address, socket_port, + discovery_port, + discovery_enabled, bootnodes, } } + + /// Get ENR bootnodes for discv5. + pub fn enr_bootnodes(&self) -> Vec> { + self.bootnodes.enrs().to_vec() + } } #[derive(Debug)] @@ -145,6 +91,7 @@ where { network_config: Arc, swarm: Swarm, + discovery: Option, peer_table: Arc>>, peer_count: Arc, outbound_p2p_requests: R, @@ -209,9 +156,36 @@ where .with_swarm_config(|_| config) .build(); + let discovery = if network_config.discovery_enabled { + let discovery_config = DiscoveryConfig::new( + network_config.socket_address, + network_config.discovery_port, + network_config.socket_port, + ) + .with_bootnodes(network_config.enr_bootnodes()); + + match DiscoveryService::new(discovery_config, &local_key).await { + Ok(disc) => { + info!( + enr = %disc.local_enr(), + "Discovery service initialized" + ); + Some(disc) + } + Err(e) => { + warn!(error = ?e, "Failed to initialize discovery service, continuing without it"); + None + } + } + } else { + info!("Discovery service disabled"); + None + }; + let mut service = Self { network_config, swarm, + discovery, peer_table: Arc::new(Mutex::new(HashMap::new())), peer_count, outbound_p2p_requests, @@ -228,11 +202,24 @@ where // Periodic reconnect attempts to bootnodes let mut reconnect_interval = interval(Duration::from_secs(30)); reconnect_interval.set_missed_tick_behavior(MissedTickBehavior::Skip); + + // Periodic discovery searches + let mut discovery_interval = interval(Duration::from_secs(30)); + discovery_interval.set_missed_tick_behavior(MissedTickBehavior::Skip); + loop { select! { _ = reconnect_interval.tick() => { self.connect_to_peers(self.network_config.bootnodes.to_multiaddrs()).await; } + _ = discovery_interval.tick() => { + // Trigger active peer discovery + if let Some(ref discovery) = self.discovery { + let known_peers = discovery.connected_peers(); + debug!(known_peers, "Triggering random peer discovery lookup"); + discovery.find_random_peers(); + } + } request = self.outbound_p2p_requests.recv() => { if let Some(request) = request { self.dispatch_outbound_request(request).await; @@ -243,6 +230,23 @@ where info!(?event, "Swarm event"); } } + enr = async { + match &mut self.discovery { + Some(disc) => disc.recv().await, + None => std::future::pending().await, + } + } => { + if let Some(enr) = enr { + if let Some(multiaddr) = DiscoveryService::enr_to_multiaddr(&enr) { + info!( + node_id = %enr.node_id(), + %multiaddr, + "Discovered peer via discv5, attempting connection" + ); + self.connect_to_peers(vec![multiaddr]).await; + } + } + } } } } @@ -659,6 +663,10 @@ where *self.swarm.local_peer_id() } + pub fn local_enr(&self) -> Option<&enr::Enr> { + self.discovery.as_ref().map(|d| d.local_enr()) + } + pub fn swarm_mut(&mut self) -> &mut Swarm { &mut self.swarm } From 5bf3f087856c56255eae5494f6317099509ada48 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:49:07 +0200 Subject: [PATCH 31/48] feat: add discovery CLI arguments --- lean_client/src/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index c32a4c5..cea50eb 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -108,6 +108,12 @@ struct Args { #[arg(short, long, default_value_t = 8083)] port: u16, + #[arg(short, long, default_value_t = 8084)] + discovery_port: u16, + + #[arg(long, default_value_t = false)] + disable_discovery: bool, + #[arg(short, long)] bootnodes: Vec, @@ -286,10 +292,14 @@ async fn main() { let mut gossipsub_config = GossipsubConfig::new(); gossipsub_config.set_topics(gossipsub_topics); + let discovery_enabled = !args.disable_discovery; + let network_service_config = Arc::new(NetworkServiceConfig::new( gossipsub_config, args.address, args.port, + args.discovery_port, + discovery_enabled, args.bootnodes, )); From 3d93fcab21a76d02316bb6facca3dde8edf8a029 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 19:49:18 +0200 Subject: [PATCH 32/48] test: add discovery protocol tests --- lean_client/networking/src/discovery/tests.rs | 1422 +++++++++++++++++ 1 file changed, 1422 insertions(+) create mode 100644 lean_client/networking/src/discovery/tests.rs diff --git a/lean_client/networking/src/discovery/tests.rs b/lean_client/networking/src/discovery/tests.rs new file mode 100644 index 0000000..6566e29 --- /dev/null +++ b/lean_client/networking/src/discovery/tests.rs @@ -0,0 +1,1422 @@ +//! Tests for Discovery v5 Protocol Specification +//! +//! Based on the official Discovery v5 specification and test vectors from: +//! https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire-test-vectors.md + +use std::net::{Ipv4Addr, Ipv6Addr}; + +/// Protocol constants matching Discovery v5 specification +mod constants { + /// Protocol identifier + pub const PROTOCOL_ID: &[u8] = b"discv5"; + /// Protocol version (v5.1) + pub const PROTOCOL_VERSION: u16 = 0x0001; + /// Maximum request ID length in bytes + pub const MAX_REQUEST_ID_LENGTH: usize = 8; + /// K-bucket size per Kademlia standard + pub const K_BUCKET_SIZE: usize = 16; + /// Alpha (lookup concurrency) + pub const ALPHA: usize = 3; + /// Number of buckets for 256-bit node ID space + pub const BUCKET_COUNT: usize = 256; + /// Request timeout in seconds (spec: 500ms) + pub const REQUEST_TIMEOUT_SECS: f64 = 0.5; + /// Handshake timeout in seconds + pub const HANDSHAKE_TIMEOUT_SECS: f64 = 1.0; + /// Maximum ENRs per NODES response + pub const MAX_NODES_RESPONSE: usize = 16; + /// Bond expiry in seconds (24 hours) + pub const BOND_EXPIRY_SECS: u64 = 86400; + /// Maximum packet size + pub const MAX_PACKET_SIZE: usize = 1280; + /// Minimum packet size + pub const MIN_PACKET_SIZE: usize = 63; +} + +/// Packet type flags +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PacketFlag { + Message = 0, + WhoAreYou = 1, + Handshake = 2, +} + +/// Message type codes matching wire protocol spec +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MessageType { + Ping = 0x01, + Pong = 0x02, + FindNode = 0x03, + Nodes = 0x04, + TalkReq = 0x05, + TalkResp = 0x06, + RegTopic = 0x07, + Ticket = 0x08, + RegConfirmation = 0x09, + TopicQuery = 0x0A, +} + +/// Request ID (variable length, max 8 bytes) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RequestId(pub Vec); + +impl RequestId { + pub fn new(data: Vec) -> Self { + assert!(data.len() <= constants::MAX_REQUEST_ID_LENGTH); + Self(data) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +/// IPv4 address (4 bytes) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IPv4(pub [u8; 4]); + +impl IPv4 { + pub fn new(bytes: [u8; 4]) -> Self { + Self(bytes) + } + + pub fn len(&self) -> usize { + 4 + } +} + +/// IPv6 address (16 bytes) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IPv6(pub [u8; 16]); + +impl IPv6 { + pub fn new(bytes: [u8; 16]) -> Self { + Self(bytes) + } + + pub fn len(&self) -> usize { + 16 + } +} + +/// ID Nonce (16 bytes / 128 bits) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IdNonce(pub [u8; 16]); + +impl IdNonce { + pub fn new(bytes: [u8; 16]) -> Self { + Self(bytes) + } + + pub fn len(&self) -> usize { + 16 + } +} + +/// Nonce (12 bytes / 96 bits) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Nonce(pub [u8; 12]); + +impl Nonce { + pub fn new(bytes: [u8; 12]) -> Self { + Self(bytes) + } + + pub fn len(&self) -> usize { + 12 + } +} + +/// Distance type (u16) +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Distance(pub u16); + +/// Port type (u16) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Port(pub u16); + +/// ENR sequence number (u64) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct SeqNumber(pub u64); + +/// Node ID (32 bytes / 256 bits) +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NodeId(pub [u8; 32]); + +impl NodeId { + pub fn new(bytes: [u8; 32]) -> Self { + Self(bytes) + } + + pub fn from_slice(slice: &[u8]) -> Self { + let mut bytes = [0u8; 32]; + bytes.copy_from_slice(slice); + Self(bytes) + } +} + +/// Discovery configuration +#[derive(Debug, Clone)] +pub struct DiscoveryConfig { + pub k_bucket_size: usize, + pub alpha: usize, + pub request_timeout_secs: f64, + pub handshake_timeout_secs: f64, + pub max_nodes_response: usize, + pub bond_expiry_secs: u64, +} + +impl Default for DiscoveryConfig { + fn default() -> Self { + Self { + k_bucket_size: constants::K_BUCKET_SIZE, + alpha: constants::ALPHA, + request_timeout_secs: constants::REQUEST_TIMEOUT_SECS, + handshake_timeout_secs: constants::HANDSHAKE_TIMEOUT_SECS, + max_nodes_response: constants::MAX_NODES_RESPONSE, + bond_expiry_secs: constants::BOND_EXPIRY_SECS, + } + } +} + +/// PING message +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Ping { + pub request_id: RequestId, + pub enr_seq: SeqNumber, +} + +/// PONG message +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Pong { + pub request_id: RequestId, + pub enr_seq: SeqNumber, + pub recipient_ip: Vec, + pub recipient_port: Port, +} + +/// FINDNODE message +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FindNode { + pub request_id: RequestId, + pub distances: Vec, +} + +/// NODES message +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Nodes { + pub request_id: RequestId, + pub total: u8, + pub enrs: Vec>, +} + +/// TALKREQ message +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TalkReq { + pub request_id: RequestId, + pub protocol: Vec, + pub request: Vec, +} + +/// TALKRESP message +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TalkResp { + pub request_id: RequestId, + pub response: Vec, +} + +/// Static header +#[derive(Debug, Clone)] +pub struct StaticHeader { + pub protocol_id: [u8; 6], + pub version: u16, + pub flag: u8, + pub nonce: Nonce, + pub authdata_size: u16, +} + +impl StaticHeader { + pub fn new(flag: u8, nonce: Nonce, authdata_size: u16) -> Self { + Self { + protocol_id: *b"discv5", + version: constants::PROTOCOL_VERSION, + flag, + nonce, + authdata_size, + } + } +} + +/// WHOAREYOU authdata +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WhoAreYouAuthdata { + pub id_nonce: IdNonce, + pub enr_seq: SeqNumber, +} + +/// Node entry in routing table +#[derive(Debug, Clone)] +pub struct NodeEntry { + pub node_id: NodeId, + pub enr_seq: SeqNumber, + pub last_seen: f64, + pub endpoint: Option, + pub verified: bool, +} + +impl NodeEntry { + pub fn new(node_id: NodeId) -> Self { + Self { + node_id, + enr_seq: SeqNumber::default(), + last_seen: 0.0, + endpoint: None, + verified: false, + } + } + + pub fn with_enr_seq(mut self, enr_seq: SeqNumber) -> Self { + self.enr_seq = enr_seq; + self + } + + pub fn with_last_seen(mut self, last_seen: f64) -> Self { + self.last_seen = last_seen; + self + } + + pub fn with_endpoint(mut self, endpoint: String) -> Self { + self.endpoint = Some(endpoint); + self + } + + pub fn with_verified(mut self, verified: bool) -> Self { + self.verified = verified; + self + } +} + +/// K-bucket for storing nodes at a specific distance +#[derive(Debug, Clone, Default)] +pub struct KBucket { + nodes: Vec, +} + +impl KBucket { + pub fn new() -> Self { + Self { nodes: Vec::new() } + } + + pub fn is_empty(&self) -> bool { + self.nodes.is_empty() + } + + pub fn is_full(&self) -> bool { + self.nodes.len() >= constants::K_BUCKET_SIZE + } + + pub fn len(&self) -> usize { + self.nodes.len() + } + + pub fn add(&mut self, entry: NodeEntry) -> bool { + // Check if node already exists + if let Some(pos) = self.nodes.iter().position(|e| e.node_id == entry.node_id) { + // Move to tail (most recent) + self.nodes.remove(pos); + self.nodes.push(entry); + return true; + } + + // Reject if full + if self.is_full() { + return false; + } + + self.nodes.push(entry); + true + } + + pub fn remove(&mut self, node_id: &NodeId) -> bool { + if let Some(pos) = self.nodes.iter().position(|e| &e.node_id == node_id) { + self.nodes.remove(pos); + true + } else { + false + } + } + + pub fn contains(&self, node_id: &NodeId) -> bool { + self.nodes.iter().any(|e| &e.node_id == node_id) + } + + pub fn get(&self, node_id: &NodeId) -> Option<&NodeEntry> { + self.nodes.iter().find(|e| &e.node_id == node_id) + } + + pub fn head(&self) -> Option<&NodeEntry> { + self.nodes.first() + } + + pub fn tail(&self) -> Option<&NodeEntry> { + self.nodes.last() + } + + pub fn iter(&self) -> impl Iterator { + self.nodes.iter() + } +} + +/// Calculate XOR distance between two node IDs +pub fn xor_distance(a: &NodeId, b: &NodeId) -> num_bigint::BigUint { + use num_bigint::BigUint; + + let a_int = BigUint::from_bytes_be(&a.0); + let b_int = BigUint::from_bytes_be(&b.0); + a_int ^ b_int +} + +/// Calculate log2 distance between two node IDs +pub fn log2_distance(a: &NodeId, b: &NodeId) -> Distance { + let xor = xor_distance(a, b); + if xor.bits() == 0 { + Distance(0) + } else { + Distance(xor.bits() as u16) + } +} + +/// Kademlia routing table +pub struct RoutingTable { + local_id: NodeId, + pub buckets: Vec, +} + +impl RoutingTable { + pub fn new(local_id: NodeId) -> Self { + let buckets = (0..constants::BUCKET_COUNT) + .map(|_| KBucket::new()) + .collect(); + Self { local_id, buckets } + } + + pub fn node_count(&self) -> usize { + self.buckets.iter().map(|b| b.len()).sum() + } + + pub fn bucket_index(&self, node_id: &NodeId) -> usize { + let dist = log2_distance(&self.local_id, node_id); + if dist.0 == 0 { + 0 + } else { + (dist.0 - 1) as usize + } + } + + pub fn add(&mut self, entry: NodeEntry) -> bool { + // Cannot add self + if entry.node_id == self.local_id { + return false; + } + + let idx = self.bucket_index(&entry.node_id); + self.buckets[idx].add(entry) + } + + pub fn remove(&mut self, node_id: &NodeId) -> bool { + let idx = self.bucket_index(node_id); + self.buckets[idx].remove(node_id) + } + + pub fn contains(&self, node_id: &NodeId) -> bool { + let idx = self.bucket_index(node_id); + self.buckets[idx].contains(node_id) + } + + pub fn get(&self, node_id: &NodeId) -> Option<&NodeEntry> { + let idx = self.bucket_index(node_id); + self.buckets[idx].get(node_id) + } + + pub fn closest_nodes(&self, target: &NodeId, count: usize) -> Vec<&NodeEntry> { + let mut all_nodes: Vec<&NodeEntry> = self + .buckets + .iter() + .flat_map(|b| b.iter()) + .collect(); + + all_nodes.sort_by(|a, b| { + let dist_a = xor_distance(&a.node_id, target); + let dist_b = xor_distance(&b.node_id, target); + dist_a.cmp(&dist_b) + }); + + all_nodes.into_iter().take(count).collect() + } + + pub fn nodes_at_distance(&self, distance: Distance) -> Vec<&NodeEntry> { + if distance.0 == 0 || distance.0 > 256 { + return Vec::new(); + } + + let idx = (distance.0 - 1) as usize; + self.buckets[idx].iter().collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use num_bigint::BigUint; + use num_traits::One; + + // ============================================================ + // Protocol Constants Tests + // ============================================================ + + mod protocol_constants { + use super::*; + + #[test] + fn test_protocol_id() { + assert_eq!(constants::PROTOCOL_ID, b"discv5"); + assert_eq!(constants::PROTOCOL_ID.len(), 6); + } + + #[test] + fn test_protocol_version() { + assert_eq!(constants::PROTOCOL_VERSION, 0x0001); + } + + #[test] + fn test_max_request_id_length() { + assert_eq!(constants::MAX_REQUEST_ID_LENGTH, 8); + } + + #[test] + fn test_k_bucket_size() { + assert_eq!(constants::K_BUCKET_SIZE, 16); + } + + #[test] + fn test_alpha_concurrency() { + assert_eq!(constants::ALPHA, 3); + } + + #[test] + fn test_bucket_count() { + assert_eq!(constants::BUCKET_COUNT, 256); + } + + #[test] + fn test_request_timeout() { + assert!((constants::REQUEST_TIMEOUT_SECS - 0.5).abs() < f64::EPSILON); + } + + #[test] + fn test_handshake_timeout() { + assert!((constants::HANDSHAKE_TIMEOUT_SECS - 1.0).abs() < f64::EPSILON); + } + + #[test] + fn test_max_nodes_response() { + assert_eq!(constants::MAX_NODES_RESPONSE, 16); + } + + #[test] + fn test_bond_expiry() { + assert_eq!(constants::BOND_EXPIRY_SECS, 86400); + } + + #[test] + fn test_packet_size_limits() { + assert_eq!(constants::MAX_PACKET_SIZE, 1280); + assert_eq!(constants::MIN_PACKET_SIZE, 63); + } + } + + // ============================================================ + // Custom Types Tests + // ============================================================ + + mod custom_types { + use super::*; + + #[test] + fn test_request_id_limit() { + let req_id = RequestId::new(vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]); + assert_eq!(req_id.len(), 8); + } + + #[test] + fn test_request_id_variable_length() { + let req_id = RequestId::new(vec![0x01]); + assert_eq!(req_id.len(), 1); + } + + #[test] + fn test_ipv4_length() { + let ip = IPv4::new([0xc0, 0xa8, 0x01, 0x01]); // 192.168.1.1 + assert_eq!(ip.len(), 4); + } + + #[test] + fn test_ipv6_length() { + let mut bytes = [0u8; 16]; + bytes[15] = 0x01; // ::1 + let ip = IPv6::new(bytes); + assert_eq!(ip.len(), 16); + } + + #[test] + fn test_id_nonce_length() { + let nonce = IdNonce::new([0x01; 16]); + assert_eq!(nonce.len(), 16); + } + + #[test] + fn test_nonce_length() { + let nonce = Nonce::new([0x01; 12]); + assert_eq!(nonce.len(), 12); + } + + #[test] + fn test_distance_type() { + let d = Distance(256); + assert_eq!(d.0, 256u16); + } + + #[test] + fn test_port_type() { + let p = Port(30303); + assert_eq!(p.0, 30303u16); + } + + #[test] + fn test_enr_seq_type() { + let seq = SeqNumber(42); + assert_eq!(seq.0, 42u64); + } + } + + // ============================================================ + // Packet Flag Tests + // ============================================================ + + mod packet_flags { + use super::*; + + #[test] + fn test_message_flag() { + assert_eq!(PacketFlag::Message as u8, 0); + } + + #[test] + fn test_whoareyou_flag() { + assert_eq!(PacketFlag::WhoAreYou as u8, 1); + } + + #[test] + fn test_handshake_flag() { + assert_eq!(PacketFlag::Handshake as u8, 2); + } + } + + // ============================================================ + // Message Types Tests + // ============================================================ + + mod message_types { + use super::*; + + #[test] + fn test_ping_type() { + assert_eq!(MessageType::Ping as u8, 0x01); + } + + #[test] + fn test_pong_type() { + assert_eq!(MessageType::Pong as u8, 0x02); + } + + #[test] + fn test_findnode_type() { + assert_eq!(MessageType::FindNode as u8, 0x03); + } + + #[test] + fn test_nodes_type() { + assert_eq!(MessageType::Nodes as u8, 0x04); + } + + #[test] + fn test_talkreq_type() { + assert_eq!(MessageType::TalkReq as u8, 0x05); + } + + #[test] + fn test_talkresp_type() { + assert_eq!(MessageType::TalkResp as u8, 0x06); + } + + #[test] + fn test_experimental_types() { + assert_eq!(MessageType::RegTopic as u8, 0x07); + assert_eq!(MessageType::Ticket as u8, 0x08); + assert_eq!(MessageType::RegConfirmation as u8, 0x09); + assert_eq!(MessageType::TopicQuery as u8, 0x0A); + } + } + + // ============================================================ + // Discovery Config Tests + // ============================================================ + + mod discovery_config { + use super::*; + + #[test] + fn test_default_values() { + let config = DiscoveryConfig::default(); + + assert_eq!(config.k_bucket_size, constants::K_BUCKET_SIZE); + assert_eq!(config.alpha, constants::ALPHA); + assert!((config.request_timeout_secs - constants::REQUEST_TIMEOUT_SECS).abs() < f64::EPSILON); + assert!((config.handshake_timeout_secs - constants::HANDSHAKE_TIMEOUT_SECS).abs() < f64::EPSILON); + assert_eq!(config.max_nodes_response, constants::MAX_NODES_RESPONSE); + assert_eq!(config.bond_expiry_secs, constants::BOND_EXPIRY_SECS); + } + + #[test] + fn test_custom_values() { + let config = DiscoveryConfig { + k_bucket_size: 8, + alpha: 5, + request_timeout_secs: 2.0, + ..Default::default() + }; + assert_eq!(config.k_bucket_size, 8); + assert_eq!(config.alpha, 5); + assert!((config.request_timeout_secs - 2.0).abs() < f64::EPSILON); + } + } + + // ============================================================ + // Ping Message Tests + // ============================================================ + + mod ping_message { + use super::*; + + #[test] + fn test_creation_with_types() { + let ping = Ping { + request_id: RequestId::new(vec![0x00, 0x00, 0x00, 0x01]), + enr_seq: SeqNumber(2), + }; + + assert_eq!(ping.request_id.0, vec![0x00, 0x00, 0x00, 0x01]); + assert_eq!(ping.enr_seq, SeqNumber(2)); + } + + #[test] + fn test_max_request_id_length() { + let ping = Ping { + request_id: RequestId::new(vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]), + enr_seq: SeqNumber(1), + }; + assert_eq!(ping.request_id.len(), 8); + } + } + + // ============================================================ + // Pong Message Tests + // ============================================================ + + mod pong_message { + use super::*; + + #[test] + fn test_creation_ipv4() { + let pong = Pong { + request_id: RequestId::new(vec![0x00, 0x00, 0x00, 0x01]), + enr_seq: SeqNumber(42), + recipient_ip: vec![0xc0, 0xa8, 0x01, 0x01], // 192.168.1.1 + recipient_port: Port(9000), + }; + + assert_eq!(pong.enr_seq, SeqNumber(42)); + assert_eq!(pong.recipient_ip.len(), 4); + assert_eq!(pong.recipient_port, Port(9000)); + } + + #[test] + fn test_creation_ipv6() { + let mut ipv6 = vec![0u8; 16]; + ipv6[15] = 0x01; // ::1 + let pong = Pong { + request_id: RequestId::new(vec![0x01]), + enr_seq: SeqNumber(1), + recipient_ip: ipv6.clone(), + recipient_port: Port(30303), + }; + + assert_eq!(pong.recipient_ip.len(), 16); + } + } + + // ============================================================ + // FindNode Message Tests + // ============================================================ + + mod findnode_message { + use super::*; + + #[test] + fn test_single_distance() { + let findnode = FindNode { + request_id: RequestId::new(vec![0x01]), + distances: vec![Distance(256)], + }; + + assert_eq!(findnode.distances, vec![Distance(256)]); + } + + #[test] + fn test_multiple_distances() { + let findnode = FindNode { + request_id: RequestId::new(vec![0x01]), + distances: vec![Distance(0), Distance(1), Distance(255), Distance(256)], + }; + + assert!(findnode.distances.contains(&Distance(0))); + assert!(findnode.distances.contains(&Distance(256))); + } + + #[test] + fn test_distance_zero_returns_self() { + let findnode = FindNode { + request_id: RequestId::new(vec![0x01]), + distances: vec![Distance(0)], + }; + assert_eq!(findnode.distances, vec![Distance(0)]); + } + } + + // ============================================================ + // Nodes Message Tests + // ============================================================ + + mod nodes_message { + use super::*; + + #[test] + fn test_single_response() { + let nodes = Nodes { + request_id: RequestId::new(vec![0x01]), + total: 1, + enrs: vec![b"enr:-example".to_vec()], + }; + + assert_eq!(nodes.total, 1); + assert_eq!(nodes.enrs.len(), 1); + } + + #[test] + fn test_multiple_responses() { + let nodes = Nodes { + request_id: RequestId::new(vec![0x01]), + total: 3, + enrs: vec![b"enr1".to_vec(), b"enr2".to_vec()], + }; + + assert_eq!(nodes.total, 3); + assert_eq!(nodes.enrs.len(), 2); + } + } + + // ============================================================ + // TalkReq Message Tests + // ============================================================ + + mod talkreq_message { + use super::*; + + #[test] + fn test_creation() { + let req = TalkReq { + request_id: RequestId::new(vec![0x01]), + protocol: b"portal".to_vec(), + request: b"payload".to_vec(), + }; + + assert_eq!(req.protocol, b"portal".to_vec()); + assert_eq!(req.request, b"payload".to_vec()); + } + } + + // ============================================================ + // TalkResp Message Tests + // ============================================================ + + mod talkresp_message { + use super::*; + + #[test] + fn test_creation() { + let resp = TalkResp { + request_id: RequestId::new(vec![0x01]), + response: b"response_data".to_vec(), + }; + + assert_eq!(resp.response, b"response_data".to_vec()); + } + + #[test] + fn test_empty_response_unknown_protocol() { + let resp = TalkResp { + request_id: RequestId::new(vec![0x01]), + response: Vec::new(), + }; + assert!(resp.response.is_empty()); + } + } + + // ============================================================ + // Static Header Tests + // ============================================================ + + mod static_header { + use super::*; + + #[test] + fn test_default_protocol_id() { + let header = StaticHeader::new(0, Nonce::new([0x00; 12]), 32); + + assert_eq!(&header.protocol_id, b"discv5"); + assert_eq!(header.version, 0x0001); + } + + #[test] + fn test_flag_values() { + for flag in [0u8, 1, 2] { + let header = StaticHeader::new(flag, Nonce::new([0xff; 12]), 32); + assert_eq!(header.flag, flag); + } + } + } + + // ============================================================ + // WhoAreYou Authdata Tests + // ============================================================ + + mod whoareyou_authdata { + use super::*; + + #[test] + fn test_creation() { + let id_nonce_bytes: [u8; 16] = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + ]; + let authdata = WhoAreYouAuthdata { + id_nonce: IdNonce::new(id_nonce_bytes), + enr_seq: SeqNumber(0), + }; + + assert_eq!(authdata.id_nonce.len(), 16); + assert_eq!(authdata.enr_seq, SeqNumber(0)); + } + } + + // ============================================================ + // XOR Distance Tests + // ============================================================ + + mod xor_distance_tests { + use super::*; + + #[test] + fn test_identical_ids_zero_distance() { + let node_id = NodeId::new([0x00; 32]); + assert_eq!(xor_distance(&node_id, &node_id), BigUint::from(0u32)); + } + + #[test] + fn test_complementary_ids_max_distance() { + let a = NodeId::new([0x00; 32]); + let b = NodeId::new([0xff; 32]); + let expected = (BigUint::one() << 256) - BigUint::one(); + assert_eq!(xor_distance(&a, &b), expected); + } + + #[test] + fn test_distance_is_symmetric() { + let a = NodeId::new([0x12; 32]); + let b = NodeId::new([0x34; 32]); + assert_eq!(xor_distance(&a, &b), xor_distance(&b, &a)); + } + + #[test] + fn test_specific_xor_values() { + let mut a_bytes = [0x00; 32]; + a_bytes[31] = 0x05; // 5 + let mut b_bytes = [0x00; 32]; + b_bytes[31] = 0x03; // 3 + let a = NodeId::new(a_bytes); + let b = NodeId::new(b_bytes); + assert_eq!(xor_distance(&a, &b), BigUint::from(6u32)); // 5 XOR 3 = 6 + } + } + + // ============================================================ + // Log2 Distance Tests + // ============================================================ + + mod log2_distance_tests { + use super::*; + + #[test] + fn test_identical_ids_return_zero() { + let node_id = NodeId::new([0x00; 32]); + assert_eq!(log2_distance(&node_id, &node_id), Distance(0)); + } + + #[test] + fn test_single_bit_difference() { + let a = NodeId::new([0x00; 32]); + let mut b_bytes = [0x00; 32]; + b_bytes[31] = 0x01; + let b = NodeId::new(b_bytes); + assert_eq!(log2_distance(&a, &b), Distance(1)); + } + + #[test] + fn test_high_bit_difference() { + let a = NodeId::new([0x00; 32]); + let mut b_bytes = [0x00; 32]; + b_bytes[31] = 0x80; // 0b10000000 + let b = NodeId::new(b_bytes); + assert_eq!(log2_distance(&a, &b), Distance(8)); + } + + #[test] + fn test_maximum_distance() { + let a = NodeId::new([0x00; 32]); + let mut b_bytes = [0x00; 32]; + b_bytes[0] = 0x80; // High bit of first byte set + let b = NodeId::new(b_bytes); + assert_eq!(log2_distance(&a, &b), Distance(256)); + } + } + + // ============================================================ + // K-Bucket Tests + // ============================================================ + + mod kbucket_tests { + use super::*; + + #[test] + fn test_new_bucket_is_empty() { + let bucket = KBucket::new(); + + assert!(bucket.is_empty()); + assert!(!bucket.is_full()); + assert_eq!(bucket.len(), 0); + } + + #[test] + fn test_add_single_node() { + let mut bucket = KBucket::new(); + let entry = NodeEntry::new(NodeId::new([0x01; 32])); + + assert!(bucket.add(entry)); + assert_eq!(bucket.len(), 1); + assert!(bucket.contains(&NodeId::new([0x01; 32]))); + } + + #[test] + fn test_bucket_capacity_limit() { + let mut bucket = KBucket::new(); + + for i in 0..constants::K_BUCKET_SIZE { + let mut bytes = [0x00; 32]; + bytes[0] = i as u8; + let entry = NodeEntry::new(NodeId::new(bytes)); + assert!(bucket.add(entry)); + } + + assert!(bucket.is_full()); + assert_eq!(bucket.len(), constants::K_BUCKET_SIZE); + + let extra = NodeEntry::new(NodeId::new([0xff; 32])); + assert!(!bucket.add(extra)); + assert_eq!(bucket.len(), constants::K_BUCKET_SIZE); + } + + #[test] + fn test_update_moves_to_tail() { + let mut bucket = KBucket::new(); + + let entry1 = NodeEntry::new(NodeId::new([0x01; 32])).with_enr_seq(SeqNumber(1)); + let entry2 = NodeEntry::new(NodeId::new([0x02; 32])).with_enr_seq(SeqNumber(1)); + bucket.add(entry1); + bucket.add(entry2); + + let updated = NodeEntry::new(NodeId::new([0x01; 32])).with_enr_seq(SeqNumber(2)); + bucket.add(updated); + + let tail = bucket.tail().unwrap(); + assert_eq!(tail.node_id, NodeId::new([0x01; 32])); + assert_eq!(tail.enr_seq, SeqNumber(2)); + } + + #[test] + fn test_remove_node() { + let mut bucket = KBucket::new(); + let entry = NodeEntry::new(NodeId::new([0x01; 32])); + bucket.add(entry); + + assert!(bucket.remove(&NodeId::new([0x01; 32]))); + assert!(bucket.is_empty()); + assert!(!bucket.contains(&NodeId::new([0x01; 32]))); + } + + #[test] + fn test_remove_nonexistent_returns_false() { + let mut bucket = KBucket::new(); + assert!(!bucket.remove(&NodeId::new([0x01; 32]))); + } + + #[test] + fn test_get_existing_node() { + let mut bucket = KBucket::new(); + let entry = NodeEntry::new(NodeId::new([0x01; 32])).with_enr_seq(SeqNumber(42)); + bucket.add(entry); + + let retrieved = bucket.get(&NodeId::new([0x01; 32])).unwrap(); + assert_eq!(retrieved.enr_seq, SeqNumber(42)); + } + + #[test] + fn test_get_nonexistent_returns_none() { + let bucket = KBucket::new(); + assert!(bucket.get(&NodeId::new([0x01; 32])).is_none()); + } + + #[test] + fn test_head_returns_oldest() { + let mut bucket = KBucket::new(); + bucket.add(NodeEntry::new(NodeId::new([0x01; 32]))); + bucket.add(NodeEntry::new(NodeId::new([0x02; 32]))); + + let head = bucket.head().unwrap(); + assert_eq!(head.node_id, NodeId::new([0x01; 32])); + } + + #[test] + fn test_tail_returns_newest() { + let mut bucket = KBucket::new(); + bucket.add(NodeEntry::new(NodeId::new([0x01; 32]))); + bucket.add(NodeEntry::new(NodeId::new([0x02; 32]))); + + let tail = bucket.tail().unwrap(); + assert_eq!(tail.node_id, NodeId::new([0x02; 32])); + } + + #[test] + fn test_iteration() { + let mut bucket = KBucket::new(); + bucket.add(NodeEntry::new(NodeId::new([0x01; 32]))); + bucket.add(NodeEntry::new(NodeId::new([0x02; 32]))); + + let node_ids: Vec<_> = bucket.iter().map(|e| e.node_id.clone()).collect(); + assert_eq!(node_ids.len(), 2); + } + } + + // ============================================================ + // Routing Table Tests + // ============================================================ + + mod routing_table_tests { + use super::*; + + #[test] + fn test_new_table_is_empty() { + let local_id = NodeId::new([0x00; 32]); + let table = RoutingTable::new(local_id); + + assert_eq!(table.node_count(), 0); + } + + #[test] + fn test_has_256_buckets() { + let local_id = NodeId::new([0x00; 32]); + let table = RoutingTable::new(local_id); + + assert_eq!(table.buckets.len(), constants::BUCKET_COUNT); + } + + #[test] + fn test_add_node() { + let local_id = NodeId::new([0x00; 32]); + let mut table = RoutingTable::new(local_id); + + let mut node_bytes = [0x00; 32]; + node_bytes[31] = 0x01; + let entry = NodeEntry::new(NodeId::new(node_bytes)); + assert!(table.add(entry.clone())); + assert_eq!(table.node_count(), 1); + assert!(table.contains(&entry.node_id)); + } + + #[test] + fn test_cannot_add_self() { + let local_id = NodeId::new([0xab; 32]); + let mut table = RoutingTable::new(local_id.clone()); + + let entry = NodeEntry::new(local_id); + assert!(!table.add(entry)); + assert_eq!(table.node_count(), 0); + } + + #[test] + fn test_bucket_assignment_by_distance() { + let local_id = NodeId::new([0x00; 32]); + let mut table = RoutingTable::new(local_id); + + let mut node_bytes = [0x00; 32]; + node_bytes[31] = 0x01; // log2 distance = 1 + let node_id = NodeId::new(node_bytes); + let entry = NodeEntry::new(node_id.clone()); + table.add(entry); + + let bucket_idx = table.bucket_index(&node_id); + assert_eq!(bucket_idx, 0); // distance 1 -> bucket 0 + assert!(table.buckets[0].contains(&node_id)); + } + + #[test] + fn test_get_existing_node() { + let local_id = NodeId::new([0x00; 32]); + let mut table = RoutingTable::new(local_id); + + let entry = NodeEntry::new(NodeId::new([0x01; 32])).with_enr_seq(SeqNumber(99)); + let node_id = entry.node_id.clone(); + table.add(entry); + + let retrieved = table.get(&node_id).unwrap(); + assert_eq!(retrieved.enr_seq, SeqNumber(99)); + } + + #[test] + fn test_remove_node() { + let local_id = NodeId::new([0x00; 32]); + let mut table = RoutingTable::new(local_id); + + let entry = NodeEntry::new(NodeId::new([0x01; 32])); + let node_id = entry.node_id.clone(); + table.add(entry); + assert!(table.remove(&node_id)); + assert!(!table.contains(&node_id)); + } + + #[test] + fn test_closest_nodes_sorted_by_distance() { + let local_id = NodeId::new([0x00; 32]); + let mut table = RoutingTable::new(local_id); + + for i in 1..5u8 { + let mut bytes = [0x00; 32]; + bytes[0] = i; + let entry = NodeEntry::new(NodeId::new(bytes)); + table.add(entry); + } + + let mut target_bytes = [0x00; 32]; + target_bytes[0] = 0x01; + let target = NodeId::new(target_bytes); + let closest = table.closest_nodes(&target, 3); + + assert_eq!(closest.len(), 3); + assert_eq!(closest[0].node_id, target); // Distance 0 to itself + } + + #[test] + fn test_closest_nodes_respects_count() { + let local_id = NodeId::new([0x00; 32]); + let mut table = RoutingTable::new(local_id); + + for i in 1..11u8 { + let mut bytes = [0x00; 32]; + bytes[0] = i; + let entry = NodeEntry::new(NodeId::new(bytes)); + table.add(entry); + } + + let mut target_bytes = [0x00; 32]; + target_bytes[0] = 0x05; + let closest = table.closest_nodes(&NodeId::new(target_bytes), 3); + assert_eq!(closest.len(), 3); + } + + #[test] + fn test_nodes_at_distance() { + let local_id = NodeId::new([0x00; 32]); + let mut table = RoutingTable::new(local_id); + + let mut node_bytes = [0x00; 32]; + node_bytes[31] = 0x01; // distance 1 + let node_id = NodeId::new(node_bytes); + let entry = NodeEntry::new(node_id.clone()); + table.add(entry); + + let nodes = table.nodes_at_distance(Distance(1)); + assert_eq!(nodes.len(), 1); + assert_eq!(nodes[0].node_id, node_id); + } + + #[test] + fn test_nodes_at_invalid_distance() { + let local_id = NodeId::new([0x00; 32]); + let table = RoutingTable::new(local_id); + + assert!(table.nodes_at_distance(Distance(0)).is_empty()); + assert!(table.nodes_at_distance(Distance(257)).is_empty()); + } + } + + // ============================================================ + // Node Entry Tests + // ============================================================ + + mod node_entry_tests { + use super::*; + + #[test] + fn test_default_values() { + let entry = NodeEntry::new(NodeId::new([0x01; 32])); + + assert_eq!(entry.node_id, NodeId::new([0x01; 32])); + assert_eq!(entry.enr_seq, SeqNumber(0)); + assert!((entry.last_seen - 0.0).abs() < f64::EPSILON); + assert!(entry.endpoint.is_none()); + assert!(!entry.verified); + } + + #[test] + fn test_full_construction() { + let entry = NodeEntry::new(NodeId::new([0x01; 32])) + .with_enr_seq(SeqNumber(42)) + .with_last_seen(1234567890.0) + .with_endpoint("192.168.1.1:30303".to_string()) + .with_verified(true); + + assert_eq!(entry.enr_seq, SeqNumber(42)); + assert_eq!(entry.endpoint, Some("192.168.1.1:30303".to_string())); + assert!(entry.verified); + } + } + + // ============================================================ + // Test Vector Tests + // ============================================================ + + mod test_vectors { + use super::*; + + // From https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire-test-vectors.md + const PING_REQUEST_ID: [u8; 4] = [0x00, 0x00, 0x00, 0x01]; + const PING_ENR_SEQ: u64 = 2; + const WHOAREYOU_ID_NONCE: [u8; 16] = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + ]; + + #[test] + fn test_ping_message_construction() { + let ping = Ping { + request_id: RequestId::new(PING_REQUEST_ID.to_vec()), + enr_seq: SeqNumber(PING_ENR_SEQ), + }; + + assert_eq!(ping.request_id.0, PING_REQUEST_ID.to_vec()); + assert_eq!(ping.enr_seq, SeqNumber(2)); + } + + #[test] + fn test_whoareyou_authdata_construction() { + let authdata = WhoAreYouAuthdata { + id_nonce: IdNonce::new(WHOAREYOU_ID_NONCE), + enr_seq: SeqNumber(0), + }; + + assert_eq!(authdata.id_nonce, IdNonce::new(WHOAREYOU_ID_NONCE)); + assert_eq!(authdata.enr_seq, SeqNumber(0)); + } + + #[test] + fn test_plaintext_message_type() { + // From AES-GCM test vector plaintext + let plaintext = hex::decode("01c20101").unwrap(); + assert_eq!(plaintext[0], MessageType::Ping as u8); + } + } + + // ============================================================ + // Packet Structure Tests + // ============================================================ + + mod packet_structure { + #[test] + fn test_static_header_size() { + // protocol-id (6) + version (2) + flag (1) + nonce (12) + authdata-size (2) + let expected_size = 6 + 2 + 1 + 12 + 2; + assert_eq!(expected_size, 23); + } + } + + // ============================================================ + // Routing with Test Vector Node IDs + // ============================================================ + + mod routing_test_vectors { + use super::*; + + // Node IDs from official test vectors (keccak256 of uncompressed pubkey) + fn node_a_id() -> NodeId { + NodeId::from_slice(&hex::decode("aaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb").unwrap()) + } + + fn node_b_id() -> NodeId { + NodeId::from_slice(&hex::decode("bbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9").unwrap()) + } + + #[test] + fn test_xor_distance_is_symmetric() { + let node_a = node_a_id(); + let node_b = node_b_id(); + + let distance = xor_distance(&node_a, &node_b); + assert!(distance > BigUint::from(0u32)); + assert_eq!(xor_distance(&node_a, &node_b), xor_distance(&node_b, &node_a)); + } + + #[test] + fn test_log2_distance_is_high() { + let node_a = node_a_id(); + let node_b = node_b_id(); + + let log_dist = log2_distance(&node_a, &node_b); + assert!(log_dist > Distance(200)); + } + } +} From a4f9dd8b79c32aa7d9cd06d076e61ee679c884d6 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 21:26:35 +0200 Subject: [PATCH 33/48] fix: update outdated readme --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5028b8f..d473719 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,13 @@ leanEthereum Consensus Client written in Rust using Grandine's libraries. Run in debug mode via terminal (with XMSS signing): ``` RUST_LOG=info ./target/release/lean_client \ - --genesis ../lean-quickstart/local-devnet/genesis/config.yaml \ - --validator-registry-path ../lean-quickstart/local-devnet/genesis/validators.yaml \ - --hash-sig-key-dir ../lean-quickstart/local-devnet/genesis/hash-sig-keys \ + --genesis ../../lean-quickstart/local-devnet/genesis/config.yaml \ + --validator-registry-path ../../lean-quickstart/local-devnet/genesis/validators.yaml \ + --hash-sig-key-dir ../../lean-quickstart/local-devnet/genesis/hash-sig-keys \ --node-id qlean_0 \ - --node-key ../lean-quickstart/local-devnet/genesis/qlean_0.key \ + --node-key ../../lean-quickstart/local-devnet/genesis/qlean_0.key \ --port 9003 \ + --disable-discovery --bootnodes "/ip4/127.0.0.1/udp/9001/quic-v1/p2p/16Uiu2HAkvi2sxT75Bpq1c7yV2FjnSQJJ432d6jeshbmfdJss1i6f" \ --bootnodes "/ip4/127.0.0.1/udp/9002/quic-v1/p2p/16Uiu2HAmPQhkD6Zg5Co2ee8ShshkiY4tDePKFARPpCS2oKSLj1E1" \ --bootnodes "/ip4/127.0.0.1/udp/9004/quic-v1/p2p/16Uiu2HAm7TYVs6qvDKnrovd9m4vvRikc4HPXm1WyLumKSe5fHxBv" From 5f700cd46f17a24feaddf8868153abf2f0695ec8 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 21:49:01 +0200 Subject: [PATCH 34/48] feat: update README.md to include instructions for testing discovery --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index d473719..81627d1 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,33 @@ leanEthereum Consensus Client written in Rust using Grandine's libraries. --bootnodes "/ip4/127.0.0.1/udp/9004/quic-v1/p2p/16Uiu2HAm7TYVs6qvDKnrovd9m4vvRikc4HPXm1WyLumKSe5fHxBv" ``` 4. Leave client running for a few minutes and observe warnings, errors, check if blocks are being justified and finalized (don't need debug mode for this last one) + +## Testing discovery + +1. Start the bootnode + + Run in the terminal: + ``` + RUST_LOG=info cargo run --features devnet2 -- \ + --port 9000 \ + --discovery-port 9100 + ``` + +2. Start the other nodes + + Run in the terminal: + ``` + RUST_LOG=info cargo run --features devnet2 -- \ + --port 9001 \ + --discovery-port 9101 \ + --bootnodes "" + ``` + + ``` + RUST_LOG=info cargo run --features devnet2 -- \ + --port 9002 \ + --discovery-port 9102 \ + --bootnodes "" + ``` + +After a minute all the nodes should be synced up and see each other From df0c3fe998bd5afacedaa670f8c09de4d152593a Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 21:51:54 +0200 Subject: [PATCH 35/48] fix: update README.md to build the client --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 81627d1..ea3b63b 100644 --- a/README.md +++ b/README.md @@ -38,27 +38,33 @@ leanEthereum Consensus Client written in Rust using Grandine's libraries. ## Testing discovery -1. Start the bootnode +1. Build the client: + ```bash + cd lean_client/ + cargo build --release + ``` + +2. Start the bootnode Run in the terminal: ``` - RUST_LOG=info cargo run --features devnet2 -- \ + RUST_LOG=info ./target/release/lean_client \ --port 9000 \ --discovery-port 9100 ``` -2. Start the other nodes +3. Start the other nodes Run in the terminal: ``` - RUST_LOG=info cargo run --features devnet2 -- \ + RUST_LOG=info ./target/release/lean_client \ --port 9001 \ --discovery-port 9101 \ --bootnodes "" ``` ``` - RUST_LOG=info cargo run --features devnet2 -- \ + RUST_LOG=info ./target/release/lean_client \ --port 9002 \ --discovery-port 9102 \ --bootnodes "" From 08953b67867da426e9817890b041eb49f7dd3641 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Sun, 18 Jan 2026 21:59:33 +0200 Subject: [PATCH 36/48] fix: format files --- lean_client/networking/src/discovery/mod.rs | 22 +++++++++-- lean_client/networking/src/discovery/tests.rs | 39 ++++++++++++------- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/lean_client/networking/src/discovery/mod.rs b/lean_client/networking/src/discovery/mod.rs index d0b67db..7ee532b 100644 --- a/lean_client/networking/src/discovery/mod.rs +++ b/lean_client/networking/src/discovery/mod.rs @@ -29,7 +29,12 @@ impl DiscoveryService { pub async fn new(config: DiscoveryConfig, keypair: &Keypair) -> Result { let enr_key = keypair_to_enr_key(keypair)?; - let local_enr = build_enr(&enr_key, config.listen_address, config.udp_port, config.libp2p_port)?; + let local_enr = build_enr( + &enr_key, + config.listen_address, + config.udp_port, + config.libp2p_port, + )?; info!( enr = %local_enr, @@ -118,7 +123,10 @@ impl DiscoveryService { } pub fn enr_to_multiaddr(enr: &Enr) -> Option { - let ip = enr.ip4().map(IpAddr::V4).or_else(|| enr.ip6().map(IpAddr::V6))?; + let ip = enr + .ip4() + .map(IpAddr::V4) + .or_else(|| enr.ip6().map(IpAddr::V6))?; let libp2p_port = enr.tcp4().or_else(|| enr.tcp6())?; let peer_id = enr_to_peer_id(enr)?; @@ -171,7 +179,12 @@ fn keypair_to_enr_key(keypair: &Keypair) -> Result { } } -fn build_enr(key: &CombinedKey, ip: IpAddr, udp_port: u16, libp2p_port: u16) -> Result> { +fn build_enr( + key: &CombinedKey, + ip: IpAddr, + udp_port: u16, + libp2p_port: u16, +) -> Result> { let mut builder = EnrBuilder::default(); // libp2p port is stored in tcp field, since Enr doesn't have a field for a quic port @@ -199,7 +212,8 @@ fn enr_to_peer_id(enr: &Enr) -> Option { match public_key { discv5::enr::CombinedPublicKey::Secp256k1(pk) => { let compressed = pk.to_sec1_bytes(); - let libp2p_pk = libp2p_identity::secp256k1::PublicKey::try_from_bytes(&compressed).ok()?; + let libp2p_pk = + libp2p_identity::secp256k1::PublicKey::try_from_bytes(&compressed).ok()?; let public = libp2p_identity::PublicKey::from(libp2p_pk); Some(PeerId::from_public_key(&public)) } diff --git a/lean_client/networking/src/discovery/tests.rs b/lean_client/networking/src/discovery/tests.rs index 6566e29..8bdbf82 100644 --- a/lean_client/networking/src/discovery/tests.rs +++ b/lean_client/networking/src/discovery/tests.rs @@ -445,11 +445,7 @@ impl RoutingTable { } pub fn closest_nodes(&self, target: &NodeId, count: usize) -> Vec<&NodeEntry> { - let mut all_nodes: Vec<&NodeEntry> = self - .buckets - .iter() - .flat_map(|b| b.iter()) - .collect(); + let mut all_nodes: Vec<&NodeEntry> = self.buckets.iter().flat_map(|b| b.iter()).collect(); all_nodes.sort_by(|a, b| { let dist_a = xor_distance(&a.node_id, target); @@ -687,8 +683,14 @@ mod tests { assert_eq!(config.k_bucket_size, constants::K_BUCKET_SIZE); assert_eq!(config.alpha, constants::ALPHA); - assert!((config.request_timeout_secs - constants::REQUEST_TIMEOUT_SECS).abs() < f64::EPSILON); - assert!((config.handshake_timeout_secs - constants::HANDSHAKE_TIMEOUT_SECS).abs() < f64::EPSILON); + assert!( + (config.request_timeout_secs - constants::REQUEST_TIMEOUT_SECS).abs() + < f64::EPSILON + ); + assert!( + (config.handshake_timeout_secs - constants::HANDSHAKE_TIMEOUT_SECS).abs() + < f64::EPSILON + ); assert_eq!(config.max_nodes_response, constants::MAX_NODES_RESPONSE); assert_eq!(config.bond_expiry_secs, constants::BOND_EXPIRY_SECS); } @@ -922,8 +924,8 @@ mod tests { #[test] fn test_creation() { let id_nonce_bytes: [u8; 16] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, ]; let authdata = WhoAreYouAuthdata { id_nonce: IdNonce::new(id_nonce_bytes), @@ -1337,8 +1339,8 @@ mod tests { const PING_REQUEST_ID: [u8; 4] = [0x00, 0x00, 0x00, 0x01]; const PING_ENR_SEQ: u64 = 2; const WHOAREYOU_ID_NONCE: [u8; 16] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, ]; #[test] @@ -1393,11 +1395,17 @@ mod tests { // Node IDs from official test vectors (keccak256 of uncompressed pubkey) fn node_a_id() -> NodeId { - NodeId::from_slice(&hex::decode("aaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb").unwrap()) + NodeId::from_slice( + &hex::decode("aaaa8419e9f49d0083561b48287df592939a8d19947d8c0ef88f2a4856a69fbb") + .unwrap(), + ) } fn node_b_id() -> NodeId { - NodeId::from_slice(&hex::decode("bbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9").unwrap()) + NodeId::from_slice( + &hex::decode("bbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9") + .unwrap(), + ) } #[test] @@ -1407,7 +1415,10 @@ mod tests { let distance = xor_distance(&node_a, &node_b); assert!(distance > BigUint::from(0u32)); - assert_eq!(xor_distance(&node_a, &node_b), xor_distance(&node_b, &node_a)); + assert_eq!( + xor_distance(&node_a, &node_b), + xor_distance(&node_b, &node_a) + ); } #[test] From 36c6e8b808124f245d7ca395bb85a736984fd1f8 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Mon, 26 Jan 2026 21:49:40 +0200 Subject: [PATCH 37/48] fix: format files --- lean_client/ENVIRONMENT_SELECTION.md | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 lean_client/ENVIRONMENT_SELECTION.md diff --git a/lean_client/ENVIRONMENT_SELECTION.md b/lean_client/ENVIRONMENT_SELECTION.md deleted file mode 100644 index d906c9d..0000000 --- a/lean_client/ENVIRONMENT_SELECTION.md +++ /dev/null @@ -1,26 +0,0 @@ -### To select which devnet you want to compile - -#### Option A -- Change the default features in root `Cargo.toml`: -```toml -[features] -default = ["devnet1", "<...other features>"] # Change to "devnet2" if needed -devnet1 = [...] -devnet2 = [...] -``` - -#### Option B -- Use the `--no-default-features` flag and specify the desired devnet feature when building or running the project: -```bash -cargo build --no-default-features --features devnet1 # Change to devnet2 -``` - - -### Running tests for a specific devnet - -From root directory, use the following command: -```bash -cargo test -p --no-default-features --features devnet1 # Change to devnet2 -``` - -Use `` to specify the crate you want to test. \ No newline at end of file From 7bef194bbf025f1f8cef0161f96feb4aff089f80 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Mon, 26 Jan 2026 21:55:25 +0200 Subject: [PATCH 38/48] fix: remove unnecessary code --- lean_client/Makefile | 2 +- .../containers/tests/unit_tests/attestation_aggregation.rs | 1 - lean_client/containers/tests/unit_tests/mod.rs | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lean_client/Makefile b/lean_client/Makefile index 72e702f..6153df6 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -42,7 +42,7 @@ check-format: .PHONY: test test: - cargo test --workspace --no-fail-fast + cargo test --workspace --all-features --no-fail-fast .PHONY: generate-test-vectors generate-test-vectors: diff --git a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs index 72d48b4..5d1e4dc 100644 --- a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs +++ b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "devnet2")] #[cfg(test)] mod tests { use containers::attestation::{ diff --git a/lean_client/containers/tests/unit_tests/mod.rs b/lean_client/containers/tests/unit_tests/mod.rs index 42747d4..315792d 100644 --- a/lean_client/containers/tests/unit_tests/mod.rs +++ b/lean_client/containers/tests/unit_tests/mod.rs @@ -10,4 +10,3 @@ mod state_justifications; mod common; mod state_process; mod state_transition; -mod attestation_aggregation; From e098f9c491d7d98c736d9c9dcba1c85369ae9464 Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Mon, 26 Jan 2026 21:56:43 +0200 Subject: [PATCH 39/48] fix: update cargo lock --- lean_client/Cargo.lock | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index bce89e2..d5c4dd4 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -3397,12 +3397,16 @@ dependencies = [ "containers", "derive_more", "discv5", + "enr", "env-config", "futures", "hex", + "k256", "libp2p", "libp2p-identity 0.2.13", "libp2p-mplex", + "num-bigint", + "num-traits", "parking_lot", "rand 0.8.5", "serde", From a142984b28f1a52349edc0f2ac76fc96d3d40a3b Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Mon, 26 Jan 2026 22:11:21 +0200 Subject: [PATCH 40/48] fix: fix code --- lean_client/networking/src/network/service.rs | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index 23d248a..cf78011 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -1,5 +1,6 @@ use std::{ collections::HashMap, + fs::File, net::IpAddr, num::{NonZeroU8, NonZeroUsize}, sync::Arc, @@ -8,6 +9,8 @@ use std::{ use anyhow::{Result, anyhow}; use containers::ssz::SszWrite; +use derive_more::Display; +use discv5::Enr; use futures::StreamExt; use libp2p::{ Multiaddr, SwarmBuilder, @@ -19,6 +22,7 @@ use libp2p::{ }; use libp2p_identity::{Keypair, PeerId}; use parking_lot::Mutex; +use serde::{Deserialize, Serialize}; use tokio::select; use tokio::time::{Duration, MissedTickBehavior, interval}; use tracing::{debug, info, trace, warn}; @@ -46,6 +50,55 @@ pub struct NetworkServiceConfig { bootnodes: StaticBootnodes, } +#[derive(Debug, Clone, Serialize, Deserialize, Display)] +#[serde(untagged)] +enum Bootnode { + Multiaddr(Multiaddr), + Enr(Enr), +} + +impl Bootnode { + fn addrs(&self) -> Vec { + match self { + Self::Multiaddr(addr) => vec![addr.clone()], + Self::Enr(enr) => enr.multiaddr_quic(), + } + } +} + +fn parse_bootnode_argument(arg: &str) -> Vec { + if let Some(value) = arg.parse::().ok() { + return vec![Bootnode::Multiaddr(value)]; + }; + + if let Some(rec) = arg.parse::().ok() { + return vec![Bootnode::Enr(rec)]; + } + + let Some(file) = File::open(&arg).ok() else { + warn!( + "value {arg:?} provided as bootnode is not recognized - it is not valid multiaddr nor valid path to file containing bootnodes." + ); + + return Vec::new(); + }; + + let bootnodes: Vec = match serde_yaml::from_reader(file) { + Ok(value) => value, + Err(err) => { + warn!("failed to read bootnodes from {arg:?}: {err:?}"); + + return Vec::new(); + } + }; + + if bootnodes.is_empty() { + warn!("provided file with bootnodes {arg:?} is empty"); + } + + bootnodes +} + impl NetworkServiceConfig { pub fn new( gossipsub_config: GossipsubConfig, @@ -55,7 +108,21 @@ impl NetworkServiceConfig { discovery_enabled: bool, bootnodes: Vec, ) -> Self { - let bootnodes = StaticBootnodes::parse(&bootnodes); + let bootnodes = StaticBootnodes::new( + bootnodes + .iter() + .flat_map(|addr_str| parse_bootnode_argument(&addr_str)) + .map(|bootnode| { + if bootnode.addrs().is_empty() { + warn!("bootnode {bootnode} doesn't have valid address to dial"); + } + match bootnode { + Bootnode::Multiaddr(addr) => crate::bootnodes::Bootnode::Multiaddr(addr), + Bootnode::Enr(enr) => crate::bootnodes::Bootnode::Enr(enr), + } + }) + .collect::>(), + ); NetworkServiceConfig { gossipsub_config, From 49181ef640e4c23ff8b797fa6696774f786ef53a Mon Sep 17 00:00:00 2001 From: Domas Klimavicius Date: Mon, 26 Jan 2026 22:44:14 +0200 Subject: [PATCH 41/48] feat: update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea3b63b..5546c2c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ leanEthereum Consensus Client written in Rust using Grandine's libraries. --node-id qlean_0 \ --node-key ../../lean-quickstart/local-devnet/genesis/qlean_0.key \ --port 9003 \ - --disable-discovery + --disable-discovery \ --bootnodes "/ip4/127.0.0.1/udp/9001/quic-v1/p2p/16Uiu2HAkvi2sxT75Bpq1c7yV2FjnSQJJ432d6jeshbmfdJss1i6f" \ --bootnodes "/ip4/127.0.0.1/udp/9002/quic-v1/p2p/16Uiu2HAmPQhkD6Zg5Co2ee8ShshkiY4tDePKFARPpCS2oKSLj1E1" \ --bootnodes "/ip4/127.0.0.1/udp/9004/quic-v1/p2p/16Uiu2HAm7TYVs6qvDKnrovd9m4vvRikc4HPXm1WyLumKSe5fHxBv" From 89c1a85da1234db363343af65a2aaa22eec2bab0 Mon Sep 17 00:00:00 2001 From: Nojus Sandovas Date: Tue, 27 Jan 2026 00:49:39 +0200 Subject: [PATCH 42/48] Fixed fork-choice tests --- lean_client/Cargo.lock | 1 + lean_client/containers/src/block.rs | 9 ++ lean_client/containers/src/public_key.rs | 42 +++--- lean_client/containers/src/state.rs | 131 +++++++++++------- lean_client/fork_choice/Cargo.toml | 1 + lean_client/fork_choice/src/handlers.rs | 53 ++++++- lean_client/fork_choice/src/store.rs | 11 +- .../tests/fork_choice_test_vectors.rs | 18 ++- .../tests/unit_tests/fork_choice.rs | 2 +- lean_client/networking/src/discovery/mod.rs | 10 +- lean_client/src/main.rs | 4 +- lean_client/validator/src/lib.rs | 48 ++++--- 12 files changed, 234 insertions(+), 96 deletions(-) diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index d5c4dd4..a8df01a 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -1642,6 +1642,7 @@ dependencies = [ "serde_json", "ssz", "test-generator", + "tracing", ] [[package]] diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index d9de1fb..3052737 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -2,6 +2,7 @@ use crate::{ Attestation, Bytes32, MultisigAggregatedSignature, Signature, Slot, State, ValidatorIndex, }; use serde::{Deserialize, Serialize}; +use ssz::SszHash; use ssz_derive::Ssz; use crate::attestation::{AggregatedAttestations, AttestationSignatures}; @@ -80,6 +81,14 @@ pub fn hash_tree_root(value: &T) -> Bytes32 { Bytes32(h) } +/// Compute the canonical block root for a Block. +/// +/// This uses the Block's own hash_tree_root, which must be consistent +/// with how latest_block_header is stored and hashed in state transitions. +pub fn compute_block_root(block: &Block) -> Bytes32 { + Bytes32(block.hash_tree_root()) +} + impl SignedBlockWithAttestation { /// Verify all XMSS signatures in this signed block. /// diff --git a/lean_client/containers/src/public_key.rs b/lean_client/containers/src/public_key.rs index 114b17c..529b00c 100644 --- a/lean_client/containers/src/public_key.rs +++ b/lean_client/containers/src/public_key.rs @@ -41,27 +41,20 @@ impl SszSize for PublicKey { // 2. Define how to write (Serialize) impl SszWrite for PublicKey { - fn write_fixed(&self, _bytes: &mut [u8]) { - panic!("SszWrite::write_fixed must be implemented for fixed-size types"); + fn write_fixed(&self, bytes: &mut [u8]) { + // Write the 52 bytes of the public key + bytes[..PUBLIC_KEY_SIZE].copy_from_slice(&self.inner); } fn write_variable(&self, _bytes: &mut Vec) -> Result<(), WriteError> { - panic!("SszWrite::write_variable must be implemented for variable-size types"); + // PublicKey is fixed-size, this should not be called + panic!("PublicKey is fixed-size, write_variable should not be called"); } fn to_ssz(&self) -> Result, WriteError> { - match Self::SIZE { - Size::Fixed { size } => { - let mut bytes = vec![0; size]; - self.write_fixed(bytes.as_mut_slice()); - Ok(bytes) - } - Size::Variable { minimum_size } => { - let mut bytes = Vec::with_capacity(minimum_size); - self.write_variable(&mut bytes)?; - Ok(bytes) - } - } + let mut bytes = vec![0u8; PUBLIC_KEY_SIZE]; + self.write_fixed(&mut bytes); + Ok(bytes) } } @@ -100,11 +93,26 @@ impl SszHash for PublicKey { type PackingFactor = typenum::U1; fn hash_tree_root(&self) -> H256 { - // Simple implementation: hash the inner bytes directly + // SSZ hash_tree_root for fixed-size types > 32 bytes: + // 1. Split into 32-byte chunks + // 2. Pad last chunk with zeros if needed + // 3. Merkleize the chunks use sha2::{Digest, Sha256}; + + // For 52 bytes: 2 chunks (32 + 20 bytes, second chunk padded to 32) + let mut chunk1 = [0u8; 32]; + let mut chunk2 = [0u8; 32]; + + chunk1.copy_from_slice(&self.inner[0..32]); + chunk2[..20].copy_from_slice(&self.inner[32..52]); + // Remaining 12 bytes of chunk2 are already zeros (padding) + + // Merkleize: hash(chunk1 || chunk2) let mut hasher = Sha256::new(); - hasher.update(&self.inner); + hasher.update(&chunk1); + hasher.update(&chunk2); let result = hasher.finalize(); + H256::from_slice(&result) } } diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index cf8db72..e61e0c2 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -320,6 +320,16 @@ impl State { let latest_header_for_hash = self.latest_block_header.clone(); let parent_root = hash_tree_root(&latest_header_for_hash); if block.parent_root != parent_root { + tracing::error!( + expected_parent_root = %format!("0x{:x}", parent_root.0), + block_parent_root = %format!("0x{:x}", block.parent_root.0), + header_slot = self.latest_block_header.slot.0, + header_proposer = self.latest_block_header.proposer_index.0, + header_parent = %format!("0x{:x}", self.latest_block_header.parent_root.0), + header_state_root = %format!("0x{:x}", self.latest_block_header.state_root.0), + header_body_root = %format!("0x{:x}", self.latest_block_header.body_root.0), + "Block parent root mismatch - debug info" + ); return Err(String::from("Block parent root mismatch")); } @@ -332,33 +342,49 @@ impl State { .push(parent_root) .expect("within limit"); - // Calculate total number of slots to track + // Calculate number of empty slots (skipped slots between parent and this block) let num_empty_slots = (block.slot.0 - self.latest_block_header.slot.0 - 1) as usize; - let new_len = self.justified_slots.len() + 1 + num_empty_slots; - - // Build new BitList with extended length - let mut new_justified_slots = JustifiedSlots::new(false, new_len); - for i in 0..self.justified_slots.len() { - if let Some(bit) = self.justified_slots.get(i) { - if *bit { - new_justified_slots.set(i, true); - } - } - } - // Set the bit for the latest block header - new_justified_slots.set( - self.justified_slots.len(), - self.latest_block_header.slot == Slot(0), - ); - // Empty slots remain false (already initialized) - // Add empty slots to historical hashes + // Add ZERO_HASH entries for empty slots to historical hashes for _ in 0..num_empty_slots { new_historical_hashes .push(Bytes32(ssz::H256::zero())) .expect("within limit"); } + // Extend justified_slots to cover slots from finalized_slot+1 to last_materialized_slot + // per leanSpec: justified_slots is stored RELATIVE to the finalized boundary + // The first entry corresponds to slot (finalized_slot + 1) + let last_materialized_slot = block.slot.0.saturating_sub(1); + let finalized_slot = self.latest_finalized.slot.0; + + let new_justified_slots = if last_materialized_slot > finalized_slot { + // Calculate relative index: slot X maps to index (X - finalized_slot - 1) + let relative_index = (last_materialized_slot - finalized_slot - 1) as usize; + let required_capacity = relative_index + 1; + let current_len = self.justified_slots.len(); + + if required_capacity > current_len { + // Extend the bitlist + let mut new_slots = JustifiedSlots::new(false, required_capacity); + // Copy existing bits + for i in 0..current_len { + if let Some(bit) = self.justified_slots.get(i) { + if *bit { + new_slots.set(i, true); + } + } + } + // New slots are initialized to false (unjustified) + new_slots + } else { + self.justified_slots.clone() + } + } else { + // last_materialized_slot <= finalized_slot: no extension needed + self.justified_slots.clone() + }; + let body_for_hash = block.body.clone(); let body_root = hash_tree_root(&body_for_hash); @@ -434,6 +460,9 @@ impl State { } /// Process a single attestation's votes. + /// + /// NOTE: justified_slots uses RELATIVE indexing. Slot X maps to index (X - finalized_slot - 1). + /// Slots at or before finalized_slot are implicitly justified (not stored in the bitlist). fn process_single_attestation( &self, vote: &crate::attestation::AttestationData, @@ -449,33 +478,31 @@ impl State { let target_root = vote.target.root; let source_root = vote.source.root; - let target_slot_int = target_slot.0 as usize; - let source_slot_int = source_slot.0 as usize; + let finalized_slot_int = initial_finalized_slot.0 as i64; - let source_is_justified = justified_slots_working - .get(source_slot_int) - .copied() - .unwrap_or(false); - let target_already_justified = justified_slots_working - .get(target_slot_int) - .copied() - .unwrap_or(false); + // Helper to check if a slot is justified using RELATIVE indexing + // Per leanSpec: slots at or before finalized_slot are implicitly justified + let is_slot_justified = |slot: Slot, justified_slots: &[bool]| -> bool { + if slot.0 as i64 <= finalized_slot_int { + // Slots at or before finalized boundary are implicitly justified + return true; + } + // Calculate relative index: slot X maps to index (X - finalized_slot - 1) + let relative_index = (slot.0 as i64 - finalized_slot_int - 1) as usize; + justified_slots.get(relative_index).copied().unwrap_or(false) + }; + + let source_is_justified = is_slot_justified(source_slot, justified_slots_working); + let target_already_justified = is_slot_justified(target_slot, justified_slots_working); + + let source_slot_int = source_slot.0 as usize; + let target_slot_int = target_slot.0 as usize; - // Special case for slot 0 (genesis): historical_block_hashes[0] is initialized as 0x0 - // in genesis, but validators attest with the actual genesis block hash (set in - // get_forkchoice_store). Allow any source_root when source is slot 0 and - // historical_block_hashes[0] is the zero hash. + // Check root matches using absolute slot for historical_block_hashes lookup let source_root_matches = self .historical_block_hashes .get(source_slot_int as u64) - .map(|r| { - if source_slot_int == 0 && r.0.is_zero() { - // Genesis slot: accept any root when historical hash is 0x0 - true - } else { - *r == source_root - } - }) + .map(|r| *r == source_root) .unwrap_or(false); let target_root_matches = self .historical_block_hashes @@ -483,9 +510,15 @@ impl State { .map(|r| *r == target_root) .unwrap_or(false); + // Ignore votes that reference zero-hash slots (per leanSpec) + if source_root.0.is_zero() || target_root.0.is_zero() { + return; + } + let is_valid_vote = source_is_justified && !target_already_justified - && (source_root_matches || target_root_matches) + && source_root_matches + && target_root_matches && target_slot > source_slot && target_slot.is_justifiable_after(initial_finalized_slot); @@ -554,11 +587,15 @@ impl State { ); *latest_justified = vote.target.clone(); - justified_slots_working.extend(std::iter::repeat_n( - false, - (target_slot_int + 1).saturating_sub(justified_slots_working.len()), - )); - justified_slots_working[target_slot_int] = true; + // Use RELATIVE indexing for justified_slots_working + // Calculate relative index for target slot + let target_relative_index = (target_slot.0 as i64 - finalized_slot_int - 1) as usize; + + // Extend the working vec if needed + if target_relative_index >= justified_slots_working.len() { + justified_slots_working.resize(target_relative_index + 1, false); + } + justified_slots_working[target_relative_index] = true; justifications.remove(&target_root); diff --git a/lean_client/fork_choice/Cargo.toml b/lean_client/fork_choice/Cargo.toml index c50bd99..f503590 100644 --- a/lean_client/fork_choice/Cargo.toml +++ b/lean_client/fork_choice/Cargo.toml @@ -10,6 +10,7 @@ default = [] env-config = { path = "../env-config", default-features = false } containers = { path = "../containers", default-features = false } ssz = { workspace = true } +tracing = "0.1" [dev-dependencies] serde_json = "1.0" diff --git a/lean_client/fork_choice/src/handlers.rs b/lean_client/fork_choice/src/handlers.rs index 5888942..353dca2 100644 --- a/lean_client/fork_choice/src/handlers.rs +++ b/lean_client/fork_choice/src/handlers.rs @@ -5,6 +5,7 @@ use containers::{ attestation::SignedAttestation, block::SignedBlockWithAttestation, Bytes32, ValidatorIndex, }; use ssz::SszHash; +use tracing::{debug, info}; #[inline] pub fn on_tick(store: &mut Store, time: u64, has_proposal: bool) { @@ -195,7 +196,7 @@ fn on_attestation_internal( /// 4. Updating the forkchoice head /// 5. Processing the proposer's attestation (as if gossiped) pub fn on_block(store: &mut Store, signed_block: SignedBlockWithAttestation) -> Result<(), String> { - let block_root = Bytes32(signed_block.message.block.hash_tree_root()); + let block_root = containers::block::compute_block_root(&signed_block.message.block); if store.blocks.contains_key(&block_root) { return Ok(()); @@ -228,6 +229,7 @@ fn process_block_internal( block_root: Bytes32, ) -> Result<(), String> { let block = signed_block.message.block.clone(); + let attestations_count = block.body.attestations.len_u64(); // Get parent state for validation let state = match store.states.get(&block.parent_root) { @@ -239,20 +241,63 @@ fn process_block_internal( } }; + // Debug: Log parent state checkpoints before transition + tracing::debug!( + block_slot = block.slot.0, + attestations_in_block = attestations_count, + parent_justified_slot = state.latest_justified.slot.0, + parent_finalized_slot = state.latest_finalized.slot.0, + justified_slots_len = state.justified_slots.len(), + "Processing block - parent state info" + ); + // Execute state transition to get post-state let new_state = state.state_transition_with_validation(signed_block.clone(), true, true)?; + // Debug: Log new state checkpoints after transition + tracing::debug!( + block_slot = block.slot.0, + new_justified_slot = new_state.latest_justified.slot.0, + new_finalized_slot = new_state.latest_finalized.slot.0, + new_justified_slots_len = new_state.justified_slots.len(), + "Block processed - new state info" + ); + // Store block and state, store the plain Block (not SignedBlockWithAttestation) store.blocks.insert(block_root, block.clone()); store.states.insert(block_root, new_state.clone()); - if new_state.latest_justified.slot > store.latest_justified.slot { + let justified_updated = new_state.latest_justified.slot > store.latest_justified.slot; + let finalized_updated = new_state.latest_finalized.slot > store.latest_finalized.slot; + + if justified_updated { + tracing::info!( + old_justified = store.latest_justified.slot.0, + new_justified = new_state.latest_justified.slot.0, + "Store justified checkpoint updated!" + ); store.latest_justified = new_state.latest_justified.clone(); } - if new_state.latest_finalized.slot > store.latest_finalized.slot { + if finalized_updated { + tracing::info!( + old_finalized = store.latest_finalized.slot.0, + new_finalized = new_state.latest_finalized.slot.0, + "Store finalized checkpoint updated!" + ); store.latest_finalized = new_state.latest_finalized.clone(); } + if !justified_updated && !finalized_updated { + tracing::debug!( + block_slot = block.slot.0, + store_justified = store.latest_justified.slot.0, + store_finalized = store.latest_finalized.slot.0, + state_justified = new_state.latest_justified.slot.0, + state_finalized = new_state.latest_finalized.slot.0, + "No checkpoint updates from this block" + ); + } + // Process block body attestations as on-chain (is_from_block=true) let signatures = &signed_block.signature; let aggregated_attestations = &block.body.attestations; @@ -331,7 +376,7 @@ fn process_pending_blocks(store: &mut Store, mut roots: Vec) { while let Some(parent_root) = roots.pop() { if let Some(purgatory) = store.blocks_queue.remove(&parent_root) { for block in purgatory { - let block_origins = Bytes32(block.message.block.hash_tree_root()); + let block_origins = containers::block::compute_block_root(&block.message.block); if let Ok(()) = process_block_internal(store, block, block_origins) { roots.push(block_origins); } diff --git a/lean_client/fork_choice/src/store.rs b/lean_client/fork_choice/src/store.rs index 07100ba..5eb27d3 100644 --- a/lean_client/fork_choice/src/store.rs +++ b/lean_client/fork_choice/src/store.rs @@ -54,8 +54,10 @@ pub fn get_forkchoice_store( ) -> Store { // Extract the plain Block from the signed block let block = anchor_block.message.block.clone(); - let block_root = Bytes32(block.hash_tree_root()); let block_slot = block.slot; + + // Compute block root using the header hash (canonical block root) + let block_root = containers::block::compute_block_root(&block); let latest_justified = if anchor_state.latest_justified.root.0.is_zero() { Checkpoint { @@ -75,6 +77,9 @@ pub fn get_forkchoice_store( anchor_state.latest_finalized.clone() }; + // Store the original anchor_state - do NOT modify it + // Modifying checkpoints would change its hash_tree_root(), breaking the + // consistency with block.state_root Store { time: block_slot.0 * INTERVALS_PER_SLOT, config, @@ -338,8 +343,8 @@ pub fn produce_block_with_signatures( Some(&store.aggregated_payloads), )?; - // Compute block root - let block_root = Bytes32(final_block.hash_tree_root()); + // Compute block root using the header hash (canonical block root) + let block_root = containers::block::compute_block_root(&final_block); // Store block and state (per devnet-2, we store the plain Block) store.blocks.insert(block_root, final_block.clone()); diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index 689d20b..802742a 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -254,7 +254,7 @@ impl Into for TestBlock { slot: Slot(self.slot), proposer_index: ValidatorIndex(self.proposer_index), parent_root: parse_root(&self.parent_root), - state_root: parse_root(&self.parent_root), + state_root: parse_root(&self.state_root), body: self.body.into(), } } @@ -323,7 +323,7 @@ struct TestAggregatedAttestation { impl Into for TestAggregatedAttestation { fn into(self) -> AggregatedAttestation { AggregatedAttestation { - aggregation_bits: self.aggregation_bits.data, + aggregation_bits: self.aggregation_bits.into(), data: self.data.into(), } } @@ -331,7 +331,17 @@ impl Into for TestAggregatedAttestation { #[derive(Debug, Deserialize)] struct TestAggregationBits { - data: AggregationBits, + data: Vec, +} + +impl Into for TestAggregationBits { + fn into(self) -> AggregationBits { + let mut bitlist = ssz::BitList::with_length(self.data.len()); + for (i, &bit) in self.data.iter().enumerate() { + bitlist.set(i, bit); + } + AggregationBits(bitlist) + } } #[derive(Debug, Deserialize)] @@ -552,7 +562,7 @@ fn forkchoice(spec_file: &str) { message: block, signature: BlockSignatures::default(), }; - let block_root = Bytes32(signed_block.message.block.hash_tree_root()); + let block_root = containers::block::compute_block_root(&signed_block.message.block); // Advance time to the block's slot to ensure attestations are processable // SECONDS_PER_SLOT is 4 (not 12) diff --git a/lean_client/fork_choice/tests/unit_tests/fork_choice.rs b/lean_client/fork_choice/tests/unit_tests/fork_choice.rs index 684a799..068f716 100644 --- a/lean_client/fork_choice/tests/unit_tests/fork_choice.rs +++ b/lean_client/fork_choice/tests/unit_tests/fork_choice.rs @@ -42,7 +42,7 @@ fn test_get_vote_target_chain() { body: BlockBody::default(), }; - let block_root = Bytes32(block.hash_tree_root()); + let block_root = containers::block::compute_block_root(&block); // Insert Block directly per leanSpec store.blocks.insert(block_root, block); diff --git a/lean_client/networking/src/discovery/mod.rs b/lean_client/networking/src/discovery/mod.rs index 7ee532b..6bceae2 100644 --- a/lean_client/networking/src/discovery/mod.rs +++ b/lean_client/networking/src/discovery/mod.rs @@ -16,6 +16,8 @@ use libp2p_identity::{Keypair, PeerId}; use tokio::sync::mpsc; use tracing::{debug, info, warn}; +use crate::enr_ext::EnrExt; + pub use config::DiscoveryConfig; /// Discovery service that wraps discv5 for peer discovery. @@ -127,7 +129,13 @@ impl DiscoveryService { .ip4() .map(IpAddr::V4) .or_else(|| enr.ip6().map(IpAddr::V6))?; - let libp2p_port = enr.tcp4().or_else(|| enr.tcp6())?; + + // Try TCP ports first (lean_client stores QUIC port in TCP field), + // then fall back to QUIC ports (genesis tools may use quic field directly) + let libp2p_port = enr.tcp4() + .or_else(|| enr.tcp6()) + .or_else(|| enr.quic4()) + .or_else(|| enr.quic6())?; let peer_id = enr_to_peer_id(enr)?; diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index cea50eb..97a9d75 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -394,7 +394,7 @@ async fn main() { match vs.build_block_proposal(&mut store, Slot(current_slot), proposer_idx) { Ok(signed_block) => { - let block_root = Bytes32(signed_block.message.block.hash_tree_root()); + let block_root = containers::block::compute_block_root(&signed_block.message.block); info!( slot = current_slot, block_root = %format!("0x{:x}", block_root.0), @@ -482,7 +482,7 @@ async fn main() { } => { let block_slot = signed_block_with_attestation.message.block.slot.0; let proposer = signed_block_with_attestation.message.block.proposer_index.0; - let block_root = Bytes32(signed_block_with_attestation.message.block.hash_tree_root()); + let block_root = containers::block::compute_block_root(&signed_block_with_attestation.message.block); let parent_root = signed_block_with_attestation.message.block.parent_root; info!( diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index f84b70a..4f5ea82 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -128,6 +128,13 @@ impl ValidatorService { ); let parent_root = get_proposal_head(store, slot); + + info!( + parent_root = %format!("0x{:x}", parent_root.0), + store_head = %format!("0x{:x}", store.head.0), + "Using parent root for block proposal" + ); + let parent_state = store .states .get(&parent_root) @@ -173,32 +180,39 @@ impl ValidatorService { // 3. Target block must be known // 4. Target is not already justified in parent state // 5. Source is justified in parent state + + // Helper: check if a slot is justified using RELATIVE indexing + // Slots at or before finalized_slot are implicitly justified + let finalized_slot = parent_state.latest_finalized.slot.0 as i64; + let is_slot_justified = |slot: Slot| -> bool { + if (slot.0 as i64) <= finalized_slot { + return true; // Implicitly justified (at or before finalized) + } + let relative_index = (slot.0 as i64 - finalized_slot - 1) as usize; + parent_state + .justified_slots + .get(relative_index) + .map(|b| *b) + .unwrap_or(false) + }; + let valid_attestations: Vec = store .latest_known_attestations .iter() .filter(|(_, data)| { - // Source must match the parent state's justified checkpoint (not store's!) - let source_matches = data.source == parent_state.latest_justified; + // Source must match the store's justified checkpoint + // (attestations are created with store.latest_justified as source) + let source_matches = data.source == store.latest_justified; // Target must be strictly after source let target_after_source = data.target.slot > data.source.slot; // Target block must be known let target_known = store.blocks.contains_key(&data.target.root); - // Check if target is NOT already justified (matching process_single_attestation) - let target_slot_idx = data.target.slot.0 as usize; - let target_already_justified = parent_state - .justified_slots - .get(target_slot_idx) - .map(|b| *b) - .unwrap_or(false); - - // Check if source is justified - let source_slot_idx = data.source.slot.0 as usize; - let source_is_justified = parent_state - .justified_slots - .get(source_slot_idx) - .map(|b| *b) - .unwrap_or(false); + // Check if target is NOT already justified (using relative indexing) + let target_already_justified = is_slot_justified(data.target.slot); + + // Check if source is justified (using relative indexing) + let source_is_justified = is_slot_justified(data.source.slot); source_matches && target_after_source From 71ba68b8dad4a05ec70a113101346915f1b40b69 Mon Sep 17 00:00:00 2001 From: Nojus Sandovas Date: Tue, 27 Jan 2026 01:11:29 +0200 Subject: [PATCH 43/48] Changes based on requested changes in PR #63 --- lean_client/containers/src/block.rs | 78 +++++++++++-------- lean_client/containers/src/state.rs | 4 +- .../containers/tests/test_vectors/runner.rs | 39 +++++----- .../tests/unit_tests/state_process.rs | 11 ++- lean_client/networking/src/network/service.rs | 12 +-- 5 files changed, 77 insertions(+), 67 deletions(-) diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index 3052737..f76551d 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -82,9 +82,6 @@ pub fn hash_tree_root(value: &T) -> Bytes32 { } /// Compute the canonical block root for a Block. -/// -/// This uses the Block's own hash_tree_root, which must be consistent -/// with how latest_block_header is stored and hashed in state transitions. pub fn compute_block_root(block: &Block) -> Bytes32 { Bytes32(block.hash_tree_root()) } @@ -133,7 +130,9 @@ impl SignedBlockWithAttestation { /// Verifies all attestation signatures using lean-multisig aggregated proofs. /// Each attestation has a single `MultisigAggregatedSignature` proof that covers /// all participating validators. - pub fn verify_signatures(&self, parent_state: State) -> bool { + /// + /// Returns `Ok(())` if all signatures are valid, or an error describing the failure. + pub fn verify_signatures(&self, parent_state: State) -> Result<(), String> { // Unpack the signed block components let block = &self.message.block; let signatures = &self.signature; @@ -141,11 +140,13 @@ impl SignedBlockWithAttestation { let attestation_signatures = signatures.attestation_signatures.clone(); // Verify signature count matches aggregated attestation count - assert_eq!( - aggregated_attestations.len_u64(), - attestation_signatures.len_u64(), - "Attestation signature groups must align with block body attestations" - ); + if aggregated_attestations.len_u64() != attestation_signatures.len_u64() { + return Err(format!( + "Attestation signature count mismatch: {} attestations vs {} signatures", + aggregated_attestations.len_u64(), + attestation_signatures.len_u64() + )); + } let validators = &parent_state.validators; let num_validators = validators.len_u64(); @@ -161,15 +162,26 @@ impl SignedBlockWithAttestation { // Ensure all validators exist in the active set for validator_id in &validator_ids { - assert!( - *validator_id < num_validators, - "Validator index out of range" - ); + if *validator_id >= num_validators { + return Err(format!( + "Validator index {} out of range (max {})", + validator_id, num_validators + )); + } } let attestation_data_root: [u8; 32] = hash_tree_root(&aggregated_attestation.data).0.into(); + // Collect validators, returning error if any not found + let mut collected_validators = Vec::with_capacity(validator_ids.len()); + for vid in &validator_ids { + let validator = validators + .get(*vid) + .map_err(|_| format!("Validator {} not found in state", vid))?; + collected_validators.push(validator); + } + // Verify the lean-multisig aggregated proof for this attestation // // The proof verifies that all validators in aggregation_bits signed @@ -177,41 +189,39 @@ impl SignedBlockWithAttestation { _aggregated_signature_proof .proof_data .verify_aggregated_payload( - &validator_ids - .iter() - .map(|vid| validators.get(*vid).expect("validator must exist")) - .collect::>(), + &collected_validators, &attestation_data_root, aggregated_attestation.data.slot.0 as u32, ) - .expect("Attestation aggregated signature verification failed"); + .map_err(|e| format!("Attestation aggregated signature verification failed: {:?}", e))?; } // Verify the proposer attestation signature (outside the attestation loop) let proposer_attestation = &self.message.proposer_attestation; let proposer_signature = &signatures.proposer_signature; - assert!( - proposer_attestation.validator_id.0 < num_validators, - "Proposer index out of range" - ); + if proposer_attestation.validator_id.0 >= num_validators { + return Err(format!( + "Proposer index {} out of range (max {})", + proposer_attestation.validator_id.0, num_validators + )); + } let proposer = validators .get(proposer_attestation.validator_id.0) - .expect("proposer must exist"); + .map_err(|_| format!("Proposer {} not found in state", proposer_attestation.validator_id.0))?; let proposer_root: [u8; 32] = hash_tree_root(&proposer_attestation.data).0.into(); - assert!( - verify_xmss_signature( - proposer.pubkey, - proposer_attestation.data.slot, - &proposer_root, - proposer_signature, - ), - "Proposer attestation signature verification failed" - ); - - true + if !verify_xmss_signature( + proposer.pubkey, + proposer_attestation.data.slot, + &proposer_root, + proposer_signature, + ) { + return Err("Proposer attestation signature verification failed".to_string()); + } + + Ok(()) } } diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index e61e0c2..6d981fa 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -566,7 +566,7 @@ impl State { if let Some(votes) = justifications.get(&target_root) { let num_validators = self.validators.len_u64() as usize; let count = votes.iter().filter(|&&v| v).count(); - let threshold = (2 * num_validators + 2) / 3; // ceil(2/3) + let threshold = (2 * num_validators).div_ceil(3); tracing::info!( target_slot = target_slot.0, @@ -583,7 +583,7 @@ impl State { tracing::info!( target_slot = target_slot.0, target_root = %format!("0x{:x}", target_root.0), - "JUSTIFICATION THRESHOLD REACHED!" + "Justification threshold reached" ); *latest_justified = vote.target.clone(); diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index 6192dad..687731a 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -683,27 +683,30 @@ impl TestRunner { if let Some(ref exception) = test_case.expect_exception { println!(" Expecting exception: {}", exception); - // Verify signatures - we expect this to fail (return false) - let result = signed_block.verify_signatures(anchor_state); - - if result { - println!(" \x1b[31m✗ FAIL: Signatures verified successfully but should have failed!\x1b[0m\n"); - return Err("Expected signature verification to fail, but it succeeded".into()); + // Verify signatures - we expect this to fail (return Err) + match signed_block.verify_signatures(anchor_state) { + Ok(()) => { + println!(" \x1b[31m✗ FAIL: Signatures verified successfully but should have failed!\x1b[0m\n"); + return Err("Expected signature verification to fail, but it succeeded".into()); + } + Err(_) => { + println!(" ✓ Correctly rejected: Invalid signatures detected"); + println!("\n\x1b[32m✓ PASS\x1b[0m\n"); + return Ok(()); + } } - - println!(" ✓ Correctly rejected: Invalid signatures detected"); - println!("\n\x1b[32m✓ PASS\x1b[0m\n"); - return Ok(()); } - let result = signed_block.verify_signatures(anchor_state); - if !result { - println!(" \x1b[31m✗ FAIL: Signature verification failed\x1b[0m\n"); - return Err("Signature verification failed".into()); + match signed_block.verify_signatures(anchor_state) { + Ok(()) => { + println!(" ✓ All signatures verified successfully"); + println!("\n\x1b[32m✓ PASS\x1b[0m\n"); + Ok(()) + } + Err(e) => { + println!(" \x1b[31m✗ FAIL: Signature verification failed: {}\x1b[0m\n", e); + Err(format!("Signature verification failed: {}", e).into()) + } } - - println!(" ✓ All signatures verified successfully"); - println!("\n\x1b[32m✓ PASS\x1b[0m\n"); - Ok(()) } } diff --git a/lean_client/containers/tests/unit_tests/state_process.rs b/lean_client/containers/tests/unit_tests/state_process.rs index 632b0b5..0c980e8 100644 --- a/lean_client/containers/tests/unit_tests/state_process.rs +++ b/lean_client/containers/tests/unit_tests/state_process.rs @@ -84,12 +84,17 @@ fn test_process_block_header_valid() { new_state.historical_block_hashes.get(0).ok(), Some(&genesis_header_root) ); - let justified_slot_0 = new_state + // After processing just the block header (no attestations), justified_slots + // uses relative indexing (slot X maps to index X - finalized_slot - 1). + // With finalized_slot = 0 and no attestations to justify slot 1, + // justified_slots should be empty or all false. + let justified_slot_1_relative = new_state .justified_slots - .get(0) + .get(0) // relative index 0 = slot 1 .map(|b| *b) .unwrap_or(false); - assert_eq!(justified_slot_0, true); + // Slot 1 is NOT justified yet (no attestations have been processed) + assert_eq!(justified_slot_1_relative, false); assert_eq!(new_state.latest_block_header.slot, Slot(1)); assert_eq!( new_state.latest_block_header.state_root, diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index cf78011..b0d37ee 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -655,11 +655,7 @@ where match signed_block_with_attestation.to_ssz() { Ok(bytes) => { if let Err(err) = self.publish_to_topic(GossipsubKind::Block, bytes) { - // Duplicate errors are expected - we receive our own blocks back from peers - let err_str = format!("{:?}", err); - if !err_str.contains("Duplicate") { - warn!(slot = slot, ?err, "Publish block with attestation failed"); - } + warn!(slot = slot, ?err, "Publish block with attestation failed"); } else { info!(slot = slot, "Broadcasted block with attestation"); } @@ -675,11 +671,7 @@ where match signed_attestation.to_ssz() { Ok(bytes) => { if let Err(err) = self.publish_to_topic(GossipsubKind::Attestation, bytes) { - // Duplicate errors are expected - we receive our own attestations back from peers - let err_str = format!("{:?}", err); - if !err_str.contains("Duplicate") { - warn!(slot = slot, ?err, "Publish attestation failed"); - } + warn!(slot = slot, ?err, "Publish attestation failed"); } else { info!(slot = slot, "Broadcasted attestation"); } From 76fcc53ad2e6644f22e4864323d61e75db5b31d4 Mon Sep 17 00:00:00 2001 From: Nojus Sandovas Date: Tue, 27 Jan 2026 02:09:56 +0200 Subject: [PATCH 44/48] Cargo fmt --- lean_client/containers/src/block.rs | 16 +++++++++++++--- lean_client/containers/src/state.rs | 12 ++++++++---- .../containers/tests/test_vectors/runner.rs | 5 ++++- .../containers/tests/unit_tests/state_process.rs | 4 ++-- lean_client/fork_choice/src/store.rs | 4 ++-- .../tests/fork_choice_test_vectors.rs | 3 ++- lean_client/networking/src/discovery/mod.rs | 5 +++-- lean_client/validator/src/lib.rs | 8 ++++---- 8 files changed, 38 insertions(+), 19 deletions(-) diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index f76551d..141aebc 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -130,7 +130,7 @@ impl SignedBlockWithAttestation { /// Verifies all attestation signatures using lean-multisig aggregated proofs. /// Each attestation has a single `MultisigAggregatedSignature` proof that covers /// all participating validators. - /// + /// /// Returns `Ok(())` if all signatures are valid, or an error describing the failure. pub fn verify_signatures(&self, parent_state: State) -> Result<(), String> { // Unpack the signed block components @@ -193,7 +193,12 @@ impl SignedBlockWithAttestation { &attestation_data_root, aggregated_attestation.data.slot.0 as u32, ) - .map_err(|e| format!("Attestation aggregated signature verification failed: {:?}", e))?; + .map_err(|e| { + format!( + "Attestation aggregated signature verification failed: {:?}", + e + ) + })?; } // Verify the proposer attestation signature (outside the attestation loop) @@ -209,7 +214,12 @@ impl SignedBlockWithAttestation { let proposer = validators .get(proposer_attestation.validator_id.0) - .map_err(|_| format!("Proposer {} not found in state", proposer_attestation.validator_id.0))?; + .map_err(|_| { + format!( + "Proposer {} not found in state", + proposer_attestation.validator_id.0 + ) + })?; let proposer_root: [u8; 32] = hash_tree_root(&proposer_attestation.data).0.into(); if !verify_xmss_signature( diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index 6d981fa..6d56e7a 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -460,7 +460,7 @@ impl State { } /// Process a single attestation's votes. - /// + /// /// NOTE: justified_slots uses RELATIVE indexing. Slot X maps to index (X - finalized_slot - 1). /// Slots at or before finalized_slot are implicitly justified (not stored in the bitlist). fn process_single_attestation( @@ -489,7 +489,10 @@ impl State { } // Calculate relative index: slot X maps to index (X - finalized_slot - 1) let relative_index = (slot.0 as i64 - finalized_slot_int - 1) as usize; - justified_slots.get(relative_index).copied().unwrap_or(false) + justified_slots + .get(relative_index) + .copied() + .unwrap_or(false) }; let source_is_justified = is_slot_justified(source_slot, justified_slots_working); @@ -589,8 +592,9 @@ impl State { // Use RELATIVE indexing for justified_slots_working // Calculate relative index for target slot - let target_relative_index = (target_slot.0 as i64 - finalized_slot_int - 1) as usize; - + let target_relative_index = + (target_slot.0 as i64 - finalized_slot_int - 1) as usize; + // Extend the working vec if needed if target_relative_index >= justified_slots_working.len() { justified_slots_working.resize(target_relative_index + 1, false); diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index 687731a..5aa84d7 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -704,7 +704,10 @@ impl TestRunner { Ok(()) } Err(e) => { - println!(" \x1b[31m✗ FAIL: Signature verification failed: {}\x1b[0m\n", e); + println!( + " \x1b[31m✗ FAIL: Signature verification failed: {}\x1b[0m\n", + e + ); Err(format!("Signature verification failed: {}", e).into()) } } diff --git a/lean_client/containers/tests/unit_tests/state_process.rs b/lean_client/containers/tests/unit_tests/state_process.rs index 0c980e8..6d93223 100644 --- a/lean_client/containers/tests/unit_tests/state_process.rs +++ b/lean_client/containers/tests/unit_tests/state_process.rs @@ -84,9 +84,9 @@ fn test_process_block_header_valid() { new_state.historical_block_hashes.get(0).ok(), Some(&genesis_header_root) ); - // After processing just the block header (no attestations), justified_slots + // After processing just the block header (no attestations), justified_slots // uses relative indexing (slot X maps to index X - finalized_slot - 1). - // With finalized_slot = 0 and no attestations to justify slot 1, + // With finalized_slot = 0 and no attestations to justify slot 1, // justified_slots should be empty or all false. let justified_slot_1_relative = new_state .justified_slots diff --git a/lean_client/fork_choice/src/store.rs b/lean_client/fork_choice/src/store.rs index 5eb27d3..f1b1e51 100644 --- a/lean_client/fork_choice/src/store.rs +++ b/lean_client/fork_choice/src/store.rs @@ -55,7 +55,7 @@ pub fn get_forkchoice_store( // Extract the plain Block from the signed block let block = anchor_block.message.block.clone(); let block_slot = block.slot; - + // Compute block root using the header hash (canonical block root) let block_root = containers::block::compute_block_root(&block); @@ -78,7 +78,7 @@ pub fn get_forkchoice_store( }; // Store the original anchor_state - do NOT modify it - // Modifying checkpoints would change its hash_tree_root(), breaking the + // Modifying checkpoints would change its hash_tree_root(), breaking the // consistency with block.state_root Store { time: block_slot.0 * INTERVALS_PER_SLOT, diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index 802742a..cd19be7 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -562,7 +562,8 @@ fn forkchoice(spec_file: &str) { message: block, signature: BlockSignatures::default(), }; - let block_root = containers::block::compute_block_root(&signed_block.message.block); + let block_root = + containers::block::compute_block_root(&signed_block.message.block); // Advance time to the block's slot to ensure attestations are processable // SECONDS_PER_SLOT is 4 (not 12) diff --git a/lean_client/networking/src/discovery/mod.rs b/lean_client/networking/src/discovery/mod.rs index 6bceae2..5d05d0b 100644 --- a/lean_client/networking/src/discovery/mod.rs +++ b/lean_client/networking/src/discovery/mod.rs @@ -129,10 +129,11 @@ impl DiscoveryService { .ip4() .map(IpAddr::V4) .or_else(|| enr.ip6().map(IpAddr::V6))?; - + // Try TCP ports first (lean_client stores QUIC port in TCP field), // then fall back to QUIC ports (genesis tools may use quic field directly) - let libp2p_port = enr.tcp4() + let libp2p_port = enr + .tcp4() .or_else(|| enr.tcp6()) .or_else(|| enr.quic4()) .or_else(|| enr.quic6())?; diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index 4f5ea82..adb17fb 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -128,13 +128,13 @@ impl ValidatorService { ); let parent_root = get_proposal_head(store, slot); - + info!( parent_root = %format!("0x{:x}", parent_root.0), store_head = %format!("0x{:x}", store.head.0), "Using parent root for block proposal" ); - + let parent_state = store .states .get(&parent_root) @@ -180,7 +180,7 @@ impl ValidatorService { // 3. Target block must be known // 4. Target is not already justified in parent state // 5. Source is justified in parent state - + // Helper: check if a slot is justified using RELATIVE indexing // Slots at or before finalized_slot are implicitly justified let finalized_slot = parent_state.latest_finalized.slot.0 as i64; @@ -195,7 +195,7 @@ impl ValidatorService { .map(|b| *b) .unwrap_or(false) }; - + let valid_attestations: Vec = store .latest_known_attestations .iter() From f0929918f53c5e44956e0c3d073b00591e01694c Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:11:51 +0200 Subject: [PATCH 45/48] Enable back signature verification tests --- .../containers/tests/test_vectors/verify_signatures.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lean_client/containers/tests/test_vectors/verify_signatures.rs b/lean_client/containers/tests/test_vectors/verify_signatures.rs index 624f7d0..7e52cff 100644 --- a/lean_client/containers/tests/test_vectors/verify_signatures.rs +++ b/lean_client/containers/tests/test_vectors/verify_signatures.rs @@ -12,11 +12,10 @@ use test_generator::test_resources; use super::runner::TestRunner; #[test_resources("test_vectors/verify_signatures/*/verify_signatures/*/*.json")] -fn verify_signatures(spec_file: &str) -> Result<(), Box> { +fn verify_signatures(spec_file: &str) { let test_path = Path::new(env!("CARGO_MANIFEST_DIR")) .join("..") .join(spec_file); - TestRunner::run_verify_signatures_test(test_path)?; - Ok(()) + TestRunner::run_verify_signatures_test(test_path).unwrap(); } From 1fc5ac33aa38fbc2238225a08e2c8652b4b9c142 Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Tue, 27 Jan 2026 14:53:10 +0200 Subject: [PATCH 46/48] add tests for proposed block --- lean_client/fork_choice/tests/unit_tests.rs | 1 + .../fork_choice/tests/unit_tests/validator.rs | 520 ++++++++++++++++++ 2 files changed, 521 insertions(+) create mode 100644 lean_client/fork_choice/tests/unit_tests/validator.rs diff --git a/lean_client/fork_choice/tests/unit_tests.rs b/lean_client/fork_choice/tests/unit_tests.rs index 480ba3f..8add67c 100644 --- a/lean_client/fork_choice/tests/unit_tests.rs +++ b/lean_client/fork_choice/tests/unit_tests.rs @@ -2,5 +2,6 @@ mod unit_tests { pub mod common; pub mod fork_choice; pub mod time; + pub mod validator; pub mod votes; } diff --git a/lean_client/fork_choice/tests/unit_tests/validator.rs b/lean_client/fork_choice/tests/unit_tests/validator.rs new file mode 100644 index 0000000..dc69cce --- /dev/null +++ b/lean_client/fork_choice/tests/unit_tests/validator.rs @@ -0,0 +1,520 @@ +//! Validator block production and attestation tests. +//! +//! Ported from spec/tests/lean_spec/subspecs/forkchoice/test_validator.py + +use super::common::create_test_store; +use containers::{ + attestation::{Attestation, AttestationData}, + block::{Block, BlockBody}, + checkpoint::Checkpoint, + config::Config, + state::State, + validator::Validator, + Bytes32, Signature, SignatureKey, Slot, Uint64, ValidatorIndex, +}; +use fork_choice::store::{ + get_vote_target, produce_block_with_signatures, update_head, Store, +}; +use ssz::SszHash; + +/// Build AttestationData matching the current store state for a given slot. +/// +/// Equivalent of Python `store.produce_attestation_data(slot)`. +fn produce_attestation_data(store: &Store, slot: Slot) -> AttestationData { + let head_block = &store.blocks[&store.head]; + let head_checkpoint = Checkpoint { + root: store.head, + slot: head_block.slot, + }; + let vote_target = get_vote_target(store); + AttestationData { + slot, + head: head_checkpoint, + target: vote_target, + source: store.latest_justified.clone(), + } +} + +/// Create a mock XMSS signature (all zeros). +fn make_mock_signature() -> Signature { + Default::default() +} + +// --------------------------------------------------------------------------- +// TestBlockProduction +// --------------------------------------------------------------------------- + +#[test] +fn test_produce_block_basic() { + let mut store = create_test_store(); + let initial_head = store.head; + + let slot = Slot(1); + let validator_idx = ValidatorIndex(1); + + let (block_root, block, _signatures) = + produce_block_with_signatures(&mut store, slot, validator_idx) + .expect("block production should succeed"); + + // Verify block structure + assert_eq!(block.slot, slot); + assert_eq!(block.proposer_index, validator_idx); + assert_eq!(block.parent_root, initial_head); + assert_ne!(block.state_root, Bytes32::default()); + + // Verify block was added to store + assert!(store.blocks.contains_key(&block_root)); + assert!(store.states.contains_key(&block_root)); +} + +#[test] +fn test_produce_block_unauthorized_proposer() { + let mut store = create_test_store(); + let slot = Slot(1); + let wrong_validator = ValidatorIndex(2); // Not proposer for slot 1 + + let result = produce_block_with_signatures(&mut store, slot, wrong_validator); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + err.contains("is not the proposer for slot"), + "unexpected error: {err}" + ); +} + +#[test] +fn test_produce_block_with_attestations() { + let mut store = create_test_store(); + let head_block = store.blocks[&store.head].clone(); + let head_checkpoint = Checkpoint { + root: store.head, + slot: head_block.slot, + }; + let target = get_vote_target(&store); + + // Add attestations for validators 5 and 6 + for vid in [5u64, 6] { + let data = AttestationData { + slot: head_block.slot, + head: head_checkpoint.clone(), + target: target.clone(), + source: store.latest_justified.clone(), + }; + store + .latest_known_attestations + .insert(ValidatorIndex(vid), data.clone()); + + let sig_key = SignatureKey { + validator_id: vid, + data_root: Bytes32(data.hash_tree_root()), + }; + store + .gossip_signatures + .insert(sig_key, make_mock_signature()); + } + + let slot = Slot(2); + let validator_idx = ValidatorIndex(2); + + let (_root, block, signatures) = + produce_block_with_signatures(&mut store, slot, validator_idx) + .expect("block production should succeed"); + + // Block should include the 2 attestations we added (validators 5 and 6). + // Attestations may be aggregated, so check the count matches signatures. + assert_eq!(block.body.attestations.len_usize(), signatures.len()); + // We added 2 attestations with identical data, so they aggregate into 1. + assert_eq!(signatures.len(), 1); + + // Verify block structure is correct + assert_eq!(block.slot, slot); + assert_eq!(block.proposer_index, validator_idx); + assert_ne!(block.state_root, Bytes32::default()); + + // Verify each aggregated signature proof + let head_state = &store.states[&store.head]; + for i in 0..block.body.attestations.len_usize() { + let agg_att = block.body.attestations.get(i as u64).unwrap(); + let proof: &containers::AggregatedSignatureProof = &signatures[i]; + assert!( + !proof.proof_data.is_empty(), + "aggregated signature proof must not be empty (placeholder detected)" + ); + let participants = proof.get_participant_indices(); + let public_keys: Vec<_> = participants + .iter() + .map(|&vid| { + head_state + .validators + .get(vid) + .expect("validator index out of range") + .pubkey + }) + .collect(); + let message: [u8; 32] = agg_att.data.data_root_bytes().0.into(); + let epoch = agg_att.data.slot.0 as u32; + proof + .proof_data + .verify(&public_keys, &message, epoch) + .expect("aggregated signature proof verification failed"); + } +} + +#[test] +fn test_produce_block_sequential_slots() { + let mut store = create_test_store(); + + // Produce block for slot 1 + let (block1_root, block1, _sig1) = + produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) + .expect("block1 should succeed"); + + // Verify first block is properly created + assert_eq!(block1.slot, Slot(1)); + assert_eq!(block1.proposer_index, ValidatorIndex(1)); + assert!(store.blocks.contains_key(&block1_root)); + assert!(store.states.contains_key(&block1_root)); + + // Without any attestations, the forkchoice will stay on genesis. + // This is the expected behavior: block1 exists but isn't the head. + // So block2 should build on genesis, not block1. + + // Produce block for slot 2 (will build on genesis due to forkchoice) + let (block2_root, block2, _sig2) = + produce_block_with_signatures(&mut store, Slot(2), ValidatorIndex(2)) + .expect("block2 should succeed"); + + // Verify block properties + assert_eq!(block2.slot, Slot(2)); + assert_eq!(block2.proposer_index, ValidatorIndex(2)); + + // The parent should be genesis (the current head), not block1 + let genesis_hash = store.head; + assert_eq!(block2.parent_root, genesis_hash); + + // Both blocks should exist in the store + assert!(store.blocks.contains_key(&block1_root)); + assert!(store.blocks.contains_key(&block2_root)); + assert!(store.blocks.contains_key(&genesis_hash)); +} + +#[test] +fn test_produce_block_empty_attestations() { + let mut store = create_test_store(); + + // Ensure no attestations in store + store.latest_known_attestations.clear(); + + let slot = Slot(3); + let validator_idx = ValidatorIndex(3); + + let (_root, block, _sig) = + produce_block_with_signatures(&mut store, slot, validator_idx) + .expect("block production should succeed"); + + // Should produce valid block with empty attestations + assert_eq!(block.body.attestations.len_usize(), 0); + assert_eq!(block.slot, slot); + assert_eq!(block.proposer_index, validator_idx); + assert_ne!(block.state_root, Bytes32::default()); +} + +#[test] +fn test_produce_block_state_consistency() { + let mut store = create_test_store(); + + // Add an attestation for validator 7 + let head_block = store.blocks[&store.head].clone(); + let head_checkpoint = Checkpoint { + root: store.head, + slot: head_block.slot, + }; + let target = get_vote_target(&store); + let data = AttestationData { + slot: head_block.slot, + head: head_checkpoint, + target, + source: store.latest_justified.clone(), + }; + store + .latest_known_attestations + .insert(ValidatorIndex(7), data.clone()); + let sig_key = SignatureKey { + validator_id: 7, + data_root: Bytes32(data.hash_tree_root()), + }; + store + .gossip_signatures + .insert(sig_key, make_mock_signature()); + + let slot = Slot(4); + let validator_idx = ValidatorIndex(4); + + let (block_root, block, signatures) = + produce_block_with_signatures(&mut store, slot, validator_idx) + .expect("block production should succeed"); + + // Verify the stored state matches the block's state root + let stored_state = &store.states[&block_root]; + assert_eq!(Bytes32(stored_state.hash_tree_root()), block.state_root); + + // Verify attestation count matches signature count. + // We added 1 attestation (validator 7), so expect exactly 1. + assert_eq!(block.body.attestations.len_usize(), signatures.len()); + assert_eq!(signatures.len(), 1); + + // Verify each aggregated signature proof + let head_state = &store.states[&store.head]; + for i in 0..block.body.attestations.len_usize() { + let agg_att = block.body.attestations.get(i as u64).unwrap(); + let proof: &containers::AggregatedSignatureProof = &signatures[i]; + assert!( + !proof.proof_data.is_empty(), + "aggregated signature proof must not be empty (placeholder detected)" + ); + let participants = proof.get_participant_indices(); + let public_keys: Vec<_> = participants + .iter() + .map(|&vid| { + head_state + .validators + .get(vid) + .expect("validator index out of range") + .pubkey + }) + .collect(); + let message: [u8; 32] = agg_att.data.data_root_bytes().0.into(); + let epoch = agg_att.data.slot.0 as u32; + proof + .proof_data + .verify(&public_keys, &message, epoch) + .expect("aggregated signature proof verification failed"); + } +} + +// --------------------------------------------------------------------------- +// TestValidatorIntegration +// --------------------------------------------------------------------------- + +#[test] +fn test_block_production_then_attestation() { + let mut store = create_test_store(); + + // Proposer produces block for slot 1 + let (_root, _block, _sig) = + produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) + .expect("block should succeed"); + + // Update store state after block production + update_head(&mut store); + + // Other validator creates attestation for slot 2 + let attestor_idx = ValidatorIndex(7); + let attestation_data = produce_attestation_data(&store, Slot(2)); + let attestation = Attestation { + validator_id: Uint64(attestor_idx.0), + data: attestation_data, + }; + + // Attestation should reference the new block as head (if it became head) + assert_eq!(attestation.validator_id, Uint64(attestor_idx.0)); + assert_eq!(attestation.data.slot, Slot(2)); + + // The attestation should be consistent with current forkchoice state + assert_eq!(attestation.data.source, store.latest_justified); +} + +#[test] +fn test_multiple_validators_coordination() { + let mut store = create_test_store(); + + // Validator 1 produces block for slot 1 + let (block1_root, block1, _sig1) = + produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) + .expect("block1 should succeed"); + let block1_hash = block1_root; + + // Validators 2-5 create attestations for slot 2 + // These will be based on the current forkchoice head (genesis) + let mut attestations = Vec::new(); + for i in 2..6u64 { + let data = produce_attestation_data(&store, Slot(2)); + let attestation = Attestation { + validator_id: Uint64(i), + data, + }; + attestations.push(attestation); + } + + // All attestations should be consistent + let first = &attestations[0]; + for att in &attestations[1..] { + assert_eq!(att.data.head.root, first.data.head.root); + assert_eq!(att.data.target.root, first.data.target.root); + assert_eq!(att.data.source.root, first.data.source.root); + } + + // Validator 2 produces next block for slot 2 + // After processing block1, head should be block1 (fork choice walks the tree) + // So block2 will build on block1 + let (block2_root, block2, _sig2) = + produce_block_with_signatures(&mut store, Slot(2), ValidatorIndex(2)) + .expect("block2 should succeed"); + + // Verify block properties + assert_eq!(block2.slot, Slot(2)); + assert_eq!(block2.proposer_index, ValidatorIndex(2)); + + // Both blocks should exist in the store + assert!(store.blocks.contains_key(&block1_hash)); + assert!(store.blocks.contains_key(&block2_root)); + + // block1 builds on genesis, block2 builds on block1 (current head) + // Get the original genesis hash from the store's blocks + let genesis_hash = store + .blocks + .iter() + .filter(|(_, b)| b.slot == Slot(0)) + .map(|(root, _)| *root) + .min() + .expect("genesis block should exist"); + assert_eq!(block1.parent_root, genesis_hash); + assert_eq!(block2.parent_root, block1_hash); +} + +#[test] +fn test_validator_edge_cases() { + let mut store = create_test_store(); + + // Test with validator index equal to number of validators - 1 + let max_validator = ValidatorIndex(9); // Last validator (0-indexed, 10 total) + let slot = Slot(9); // This validator's slot + + // Should be able to produce block + let (_root, block, _sig) = + produce_block_with_signatures(&mut store, slot, max_validator) + .expect("max validator block should succeed"); + assert_eq!(block.proposer_index, max_validator); + + // Should be able to produce attestation + let attestation_data = produce_attestation_data(&store, Slot(10)); + let attestation = Attestation { + validator_id: Uint64(max_validator.0), + data: attestation_data, + }; + assert_eq!(attestation.validator_id, Uint64(max_validator.0)); +} + +#[test] +fn test_validator_operations_empty_store() { + use containers::block::BlockWithAttestation; + use containers::block::SignedBlockWithAttestation; + use fork_choice::store::get_forkchoice_store; + + let config = Config { genesis_time: 1000 }; + + // Create validators list with 3 validators + let validators = vec![Validator::default(); 3]; + let state = State::generate_genesis_with_validators(Uint64(1000), validators); + + let genesis_body = BlockBody::default(); + let genesis = Block { + slot: Slot(0), + proposer_index: ValidatorIndex(0), + parent_root: Bytes32::default(), + state_root: Bytes32(state.hash_tree_root()), + body: genesis_body, + }; + + let block_with_attestation = BlockWithAttestation { + block: genesis.clone(), + proposer_attestation: Attestation::default(), + }; + + let signed_block = SignedBlockWithAttestation { + message: block_with_attestation, + signature: Default::default(), + }; + + let mut store = get_forkchoice_store(state, signed_block, config); + + // Should be able to produce block and attestation + let (_root, block, _sig) = + produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) + .expect("block should succeed"); + let attestation_data = produce_attestation_data(&store, Slot(1)); + let attestation = Attestation { + validator_id: Uint64(2), + data: attestation_data, + }; + + assert_eq!(block.slot, Slot(1)); + assert_eq!(attestation.validator_id, Uint64(2)); +} + +// --------------------------------------------------------------------------- +// TestValidatorErrorHandling +// --------------------------------------------------------------------------- + +#[test] +fn test_produce_block_wrong_proposer() { + let mut store = create_test_store(); + let slot = Slot(5); + let wrong_proposer = ValidatorIndex(3); // Should be validator 5 for slot 5 + + let result = produce_block_with_signatures(&mut store, slot, wrong_proposer); + assert!(result.is_err()); + assert!(result.unwrap_err().contains("is not the proposer for slot")); +} + +#[test] +fn test_produce_block_missing_parent_state() { + let checkpoint = Checkpoint { + root: Bytes32(ssz::H256::from_slice(&[0xab; 32])), + slot: Slot(0), + }; + + // Create store with missing parent state + let store = Store { + time: 100, + config: Config { genesis_time: 1000 }, + head: Bytes32(ssz::H256::from_slice(&[0xab; 32])), + safe_target: Bytes32(ssz::H256::from_slice(&[0xab; 32])), + latest_justified: checkpoint.clone(), + latest_finalized: checkpoint, + blocks: Default::default(), + states: Default::default(), + ..Default::default() + }; + + // Missing head in get_proposal_head -> KeyError equivalent + let result = std::panic::catch_unwind(|| { + let mut s = store; + produce_block_with_signatures(&mut s, Slot(1), ValidatorIndex(1)) + }); + assert!(result.is_err()); +} + +#[test] +fn test_validator_operations_invalid_parameters() { + let store = create_test_store(); + let genesis_hash = store.head; + let state = &store.states[&genesis_hash]; + let num_validators = state.validators.len_u64(); + + // Very large validator index (should work mathematically) + let large_validator = ValidatorIndex(1_000_000); + let large_slot = Slot(1_000_000); + + // is_proposer_for should work (though likely return False) + let result = large_slot.0 % num_validators == large_validator.0; + let _: bool = result; + + // Attestation can be created for any validator + let attestation_data = produce_attestation_data(&store, Slot(1)); + let attestation = Attestation { + validator_id: Uint64(large_validator.0), + data: attestation_data, + }; + assert_eq!(attestation.validator_id, Uint64(large_validator.0)); +} From 7f91dae7e43ab9ff680ae44f97e652be552d294b Mon Sep 17 00:00:00 2001 From: sirse Date: Thu, 29 Jan 2026 23:28:48 +0200 Subject: [PATCH 47/48] Fix XMSS signature aggregation Fixed XMSS signature aggregation, as well as moved all xmss-related code to xmss crate. --- lean_client/Cargo.lock | 94 +- lean_client/Cargo.toml | 287 +++- lean_client/Makefile | 4 +- lean_client/chain/Cargo.toml | 7 +- lean_client/containers/Cargo.toml | 43 +- lean_client/containers/src/attestation.rs | 267 +--- lean_client/containers/src/block.rs | 186 +-- lean_client/containers/src/checkpoint.rs | 38 +- lean_client/containers/src/config.rs | 6 +- lean_client/containers/src/lib.rs | 44 +- lean_client/containers/src/public_key.rs | 215 --- lean_client/containers/src/serde_helpers.rs | 147 +- lean_client/containers/src/signature.rs | 121 -- lean_client/containers/src/slot.rs | 2 +- lean_client/containers/src/state.rs | 544 +++---- lean_client/containers/src/status.rs | 2 +- .../containers/src/test_vectors/mod.rs | 23 - lean_client/containers/src/types.rs | 60 - lean_client/containers/src/validator.rs | 16 +- .../containers/tests/test_vectors/mod.rs | 2 +- .../containers/tests/test_vectors/runner.rs | 76 +- .../tests/test_vectors/verify_signatures.rs | 2 +- .../unit_tests/attestation_aggregation.rs | 40 +- .../containers/tests/unit_tests/common.rs | 42 +- .../tests/unit_tests/state_basic.rs | 32 +- .../tests/unit_tests/state_justifications.rs | 42 +- .../tests/unit_tests/state_process.rs | 53 +- .../tests/unit_tests/state_transition.rs | 33 +- lean_client/fork_choice/Cargo.toml | 22 +- lean_client/fork_choice/src/handlers.rs | 159 +- lean_client/fork_choice/src/store.rs | 91 +- .../tests/fork_choice_test_vectors.rs | 75 +- .../fork_choice/tests/unit_tests/common.rs | 20 +- .../tests/unit_tests/fork_choice.rs | 15 +- .../fork_choice/tests/unit_tests/time.rs | 3 +- .../fork_choice/tests/unit_tests/validator.rs | 215 +-- .../fork_choice/tests/unit_tests/votes.rs | 55 +- lean_client/networking/Cargo.toml | 55 +- .../networking/src/gossipsub/message.rs | 2 +- lean_client/networking/src/network/service.rs | 12 +- lean_client/networking/src/req_resp.rs | 12 +- lean_client/networking/src/types.rs | 9 +- lean_client/src/main.rs | 92 +- ...ation_accumulation_full_validator_set.json | 2 +- ...ttestation_superseding_same_validator.json | 2 +- ...stations_move_to_known_between_blocks.json | 2 +- ...chain_attestation_superseding_pattern.json | 2 +- ...ser_attestation_appears_in_latest_new.json | 2 +- ...lot_gaps_with_attestation_superseding.json | 2 +- ...ion_target_advances_with_attestations.json | 2 +- ...testation_target_at_genesis_initially.json | 2 +- ...station_target_justifiable_constraint.json | 2 +- ...ttestation_target_with_extended_chain.json | 2 +- ...est_attestation_target_with_slot_gaps.json | 2 +- ...test_head_advances_through_deep_chain.json | 2 +- .../test_head_switches_to_heavier_fork.json | 2 +- .../test_head_with_deep_fork_split.json | 2 +- .../test_head_with_gaps_in_slots.json | 2 +- .../test_head_with_large_gaps.json | 2 +- .../test_head_with_two_competing_forks.json | 2 +- ...test_back_and_forth_reorg_oscillation.json | 2 +- .../test_reorg_on_newly_justified_slot.json | 2 +- ..._heavy_fork_resists_light_competition.json | 2 +- .../test_reorg_with_slot_gaps.json | 2 +- .../test_simple_one_block_reorg.json | 2 +- .../test_three_block_deep_reorg.json | 2 +- .../test_three_way_fork_competition.json | 2 +- ..._two_block_reorg_progressive_building.json | 2 +- ...ht_forks_use_lexicographic_tiebreaker.json | 2 +- .../test_block_at_large_slot_number.json | 2 +- .../test_block_extends_deep_chain.json | 2 +- .../test_block_with_invalid_parent_root.json | 2 +- .../test_block_with_invalid_proposer.json | 2 +- .../test_block_with_invalid_state_root.json | 2 +- .../test_block_with_wrong_slot.json | 2 +- .../test_blocks_with_gaps.json | 2 +- .../test_empty_blocks.json | 2 +- .../test_empty_blocks_with_missed_slots.json | 2 +- .../test_linear_chain_multiple_blocks.json | 2 +- ...est_process_first_block_after_genesis.json | 2 +- .../test_genesis_custom_time.json | 2 +- .../test_genesis_custom_validator_set.json | 2 +- .../test_genesis_default_configuration.json | 2 +- ...alid_aggregated_attestation_signature.json | 1346 +++++++++++++++++ .../test_invalid_proposer_signature.json | 1268 ++++++++++++++++ .../test_invalid_signature.json | 114 -- ...test_proposer_and_attester_signatures.json | 6 +- .../test_proposer_signature.json | 2 +- lean_client/validator/Cargo.toml | 25 +- lean_client/validator/src/keys.rs | 95 +- lean_client/validator/src/lib.rs | 74 +- lean_client/xmss/Cargo.toml | 23 + lean_client/xmss/src/aggregated_signature.rs | 163 ++ lean_client/xmss/src/lib.rs | 9 + lean_client/xmss/src/public_key.rs | 118 ++ lean_client/xmss/src/secret_key.rs | 66 + lean_client/xmss/src/signature.rs | 202 +++ lean_client/xmss/tests/aggregate.rs | 28 + lean_client/xmss/tests/public_key.rs | 15 + lean_client/xmss/tests/signing.rs | 14 + 100 files changed, 4604 insertions(+), 2309 deletions(-) delete mode 100644 lean_client/containers/src/public_key.rs delete mode 100644 lean_client/containers/src/signature.rs delete mode 100644 lean_client/containers/src/test_vectors/mod.rs delete mode 100644 lean_client/containers/src/types.rs create mode 100644 lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_aggregated_attestation_signature.json create mode 100644 lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_proposer_signature.json delete mode 100644 lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json create mode 100644 lean_client/xmss/Cargo.toml create mode 100644 lean_client/xmss/src/aggregated_signature.rs create mode 100644 lean_client/xmss/src/lib.rs create mode 100644 lean_client/xmss/src/public_key.rs create mode 100644 lean_client/xmss/src/secret_key.rs create mode 100644 lean_client/xmss/src/signature.rs create mode 100644 lean_client/xmss/tests/aggregate.rs create mode 100644 lean_client/xmss/tests/public_key.rs create mode 100644 lean_client/xmss/tests/signing.rs diff --git a/lean_client/Cargo.lock b/lean_client/Cargo.lock index a8df01a..1203b29 100644 --- a/lean_client/Cargo.lock +++ b/lean_client/Cargo.lock @@ -198,7 +198,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arithmetic" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" +source = "git+https://github.com/grandinetech/grandine?rev=64afdee3c6be79fceffb66933dcb69a943f3f1ae#64afdee3c6be79fceffb66933dcb69a943f3f1ae" dependencies = [ "easy-ext", "typenum", @@ -762,7 +762,7 @@ dependencies = [ [[package]] name = "chain" -version = "0.1.0" +version = "0.0.0" [[package]] name = "chrono" @@ -907,15 +907,11 @@ dependencies = [ [[package]] name = "containers" -version = "0.1.0" +version = "0.0.0" dependencies = [ - "alloy-primitives", "anyhow", "env-config", - "ethereum_ssz", "hex", - "lean-multisig", - "leansig 0.1.0 (git+https://github.com/leanEthereum/leanSig?rev=73bedc26ed961b110df7ac2e234dc11361a4bf25)", "pretty_assertions", "rstest", "serde", @@ -923,10 +919,11 @@ dependencies = [ "serde_yaml", "sha2 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)", "ssz", - "ssz_derive", "test-generator", "tracing", + "try_from_iterator", "typenum", + "xmss", ] [[package]] @@ -1633,16 +1630,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] -name = "fork-choice" -version = "0.1.0" +name = "fork_choice" +version = "0.0.0" dependencies = [ + "anyhow", "containers", "env-config", + "rand 0.9.2", + "rand_chacha 0.9.0", "serde", "serde_json", "ssz", "test-generator", "tracing", + "xmss", ] [[package]] @@ -1912,7 +1913,7 @@ dependencies = [ [[package]] name = "hashing" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" +source = "git+https://github.com/grandinetech/grandine?rev=64afdee3c6be79fceffb66933dcb69a943f3f1ae#64afdee3c6be79fceffb66933dcb69a943f3f1ae" dependencies = [ "ethereum-types", "generic-array", @@ -2501,14 +2502,17 @@ dependencies = [ "chain", "clap", "containers", - "fork-choice", + "ethereum-types", + "fork_choice", "hex", "libp2p-identity 0.2.13", "networking", + "ssz", "tokio", "tracing", "tracing-subscriber", "validator", + "xmss", ] [[package]] @@ -2587,26 +2591,6 @@ dependencies = [ "whir-p3", ] -[[package]] -name = "leansig" -version = "0.1.0" -source = "git+https://github.com/leanEthereum/leanSig?branch=main#73bedc26ed961b110df7ac2e234dc11361a4bf25" -dependencies = [ - "dashmap", - "ethereum_ssz", - "num-bigint", - "num-traits", - "p3-baby-bear 0.4.1", - "p3-field 0.4.1", - "p3-koala-bear 0.4.1", - "p3-symmetric 0.4.1", - "rand 0.9.2", - "rayon", - "serde", - "sha3", - "thiserror 2.0.17", -] - [[package]] name = "leansig" version = "0.1.0" @@ -3390,9 +3374,8 @@ dependencies = [ [[package]] name = "networking" -version = "0.1.0" +version = "0.0.0" dependencies = [ - "alloy-primitives", "anyhow", "async-trait", "containers", @@ -3409,7 +3392,7 @@ dependencies = [ "num-bigint", "num-traits", "parking_lot", - "rand 0.8.5", + "rand 0.9.2", "serde", "serde_yaml", "sha2 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4503,7 +4486,7 @@ dependencies = [ "lean_compiler", "lean_prover", "lean_vm", - "leansig 0.1.0 (git+https://github.com/leanEthereum/leanSig?rev=73bedc26ed961b110df7ac2e234dc11361a4bf25)", + "leansig", "lookup", "multilinear-toolkit", "p3-challenger 0.3.0", @@ -4959,7 +4942,7 @@ dependencies = [ [[package]] name = "serde_utils" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" +source = "git+https://github.com/grandinetech/grandine?rev=64afdee3c6be79fceffb66933dcb69a943f3f1ae#64afdee3c6be79fceffb66933dcb69a943f3f1ae" dependencies = [ "const-hex", "generic-array", @@ -5169,7 +5152,7 @@ dependencies = [ [[package]] name = "ssz" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" +source = "git+https://github.com/grandinetech/grandine?rev=64afdee3c6be79fceffb66933dcb69a943f3f1ae#64afdee3c6be79fceffb66933dcb69a943f3f1ae" dependencies = [ "arithmetic", "bit_field", @@ -5201,7 +5184,7 @@ dependencies = [ [[package]] name = "ssz_derive" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" +source = "git+https://github.com/grandinetech/grandine?rev=64afdee3c6be79fceffb66933dcb69a943f3f1ae#64afdee3c6be79fceffb66933dcb69a943f3f1ae" dependencies = [ "darling", "easy-ext", @@ -5227,7 +5210,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "std_ext" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" +source = "git+https://github.com/grandinetech/grandine?rev=64afdee3c6be79fceffb66933dcb69a943f3f1ae#64afdee3c6be79fceffb66933dcb69a943f3f1ae" dependencies = [ "easy-ext", "triomphe", @@ -5710,7 +5693,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "try_from_iterator" version = "0.0.0" -source = "git+https://github.com/grandinetech/grandine?branch=develop#41fdb2f1595eb48e328ab6e43835e6df5376fc8b" +source = "git+https://github.com/grandinetech/grandine?rev=64afdee3c6be79fceffb66933dcb69a943f3f1ae#64afdee3c6be79fceffb66933dcb69a943f3f1ae" [[package]] name = "typenum" @@ -5869,15 +5852,19 @@ dependencies = [ [[package]] name = "validator" -version = "0.1.0" +version = "0.0.0" dependencies = [ + "anyhow", "containers", "env-config", - "fork-choice", - "leansig 0.1.0 (git+https://github.com/leanEthereum/leanSig?branch=main)", + "ethereum-types", + "fork_choice", "serde_yaml", + "ssz", "tracing", "typenum", + "xmss", + "zeroize", ] [[package]] @@ -6470,6 +6457,25 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "xmss" +version = "0.0.0" +dependencies = [ + "anyhow", + "derive_more", + "ethereum-types", + "ethereum_ssz", + "hex", + "lean-multisig", + "leansig", + "rand 0.9.2", + "rand_chacha 0.9.0", + "serde", + "ssz", + "typenum", + "zeroize", +] + [[package]] name = "yamux" version = "0.12.1" diff --git a/lean_client/Cargo.toml b/lean_client/Cargo.toml index 21fad13..9ea212d 100644 --- a/lean_client/Cargo.toml +++ b/lean_client/Cargo.toml @@ -1,19 +1,236 @@ [workspace] -members = ["chain", "containers", "env-config", "fork_choice", "networking", "validator"] -resolver = "2" +members = ["chain", "containers", "env-config", "fork_choice", "networking", "validator", "xmss"] +resolver = "3" [workspace.package] version = "0.1.0" edition = "2024" -authors = ["TODO"] +authors = ["Grandine Team"] license = "MIT OR Apache-2.0" +# Lints are copied from [main grandine repo](https://github.com/grandinetech/grandine.git). +[workspace.lints.rust] +unsafe_code = 'forbid' + +# A subset of `rustc` lints that are allowed by default. +# A few notable ones that we do not enable: +# +# - `elided_lifetimes_in_paths` +# It hurts readability and doesn't provide a clear benefit. +# +# - `missing_copy_implementations` +# This would be more useful if it only triggered for types that are `Clone` but not `Copy`. +# +# - `variant_size_differences` +# `clippy::large_enum_variant` does nearly the same thing and is enabled by default. +# +# See the output of `rustc --warn help` for a full list of lints available in the current version. +# They are documented at . +absolute_paths_not_starting_with_crate = 'warn' +anonymous_parameters = 'warn' +deprecated_in_future = 'warn' +deprecated_safe = { level = 'warn', priority = -1 } +let_underscore_drop = 'warn' +macro_use_extern_crate = 'warn' +meta_variable_misuse = 'warn' +missing_unsafe_on_extern = 'warn' +non_ascii_idents = 'warn' +non_local_definitions = 'warn' +redundant_lifetimes = 'warn' +trivial_casts = 'warn' +trivial_numeric_casts = 'warn' +unit_bindings = 'warn' +unused_crate_dependencies = 'warn' +unused_extern_crates = 'warn' +unused_import_braces = 'warn' +unused_lifetimes = 'warn' +unused_macro_rules = 'warn' +unused_qualifications = 'warn' + +# These are almost never helpful and require boilerplate. +unstable_name_collisions = 'allow' + +[workspace.lints.clippy] +# Additional Clippy lint groups. +nursery = 'warn' +pedantic = 'warn' + +# A subset of the `clippy::cargo` group. +negative_feature_names = 'warn' +redundant_feature_names = 'warn' +wildcard_dependencies = 'warn' + +# A subset of the `clippy::restriction` group. +# Some notable lints from it that we do not enable: +# +# - `clippy::absolute_paths` +# It is triggered by functions, which contradicts the Rust convention of qualifying them. +# +# - `clippy::arithmetic_side_effects` +# It's the static equivalent of `overflow-checks` in `Cargo.toml`, but it hurts readability. +# +# - `clippy::error_impl_error` +# It's unidiomatic. +# +# - `clippy::infinite_loop` +# It is triggered by functions that return `core::convert::Infallible` or +# types parameterized with it like `anyhow::Result`. +# +# - `clippy::integer_division_remainder_used` +# Most of our code is not cryptographic. +# +# - `clippy::iter_over_hash_type` +# Iteration order often does not matter. +# `HashMap`s and `HashSet`s tend to be faster than their ordered counterparts. +# On the other hand, enabling this produces surprisingly few warnings. +# We could easily rewrite the offending code to pass. +# +# - `clippy::min_ident_chars` +# It's unidiomatic. +# It's even triggered by identifiers imported from other crates. +# +# - `clippy::mem_forget` +# Setting it to deny (as opposed to forbid) makes no sense. +# `core::mem::forget` is impossible to use by mistake. +# +# - `clippy::renamed_function_params` +# It's unidiomatic. +# +# - `clippy::single_call_fn`. +# It's unidiomatic and conflicts with lints like `clippy::too_many_lines`. +# Public functions are not exempt from it if `avoid-breaking-exported-api` is `false`. +# +# - `clippy::std_instead_of_alloc` +# It would require adding `extern crate alloc;` everywhere. +# +# - `clippy::tests_outside_test_module` +# It is triggered by integration tests. +# +# - `clippy::unimplemented` +# It's useful to leave some trait methods unimplemented. +alloc_instead_of_core = 'warn' +allow_attributes = 'warn' +assertions_on_result_states = 'warn' +cfg_not_test = 'warn' +clone_on_ref_ptr = 'warn' +dbg_macro = 'warn' +decimal_literal_representation = 'warn' +empty_drop = 'warn' +empty_enum_variants_with_brackets = 'warn' +empty_structs_with_brackets = 'warn' +filetype_is_file = 'warn' +float_arithmetic = 'warn' +float_cmp_const = 'warn' +format_push_string = 'warn' +get_unwrap = 'warn' +host_endian_bytes = 'warn' +if_then_some_else_none = 'warn' +lossy_float_literal = 'warn' +missing_asserts_for_indexing = 'warn' +mixed_read_write_in_expression = 'warn' +multiple_inherent_impl = 'warn' +mutex_atomic = 'warn' +needless_raw_strings = 'warn' +partial_pub_fields = 'warn' +print_stderr = 'warn' +print_stdout = 'warn' +pub_without_shorthand = 'warn' +rc_buffer = 'warn' +rc_mutex = 'warn' +redundant_type_annotations = 'warn' +rest_pat_in_fully_bound_structs = 'warn' +same_name_method = 'warn' +semicolon_inside_block = 'warn' +std_instead_of_core = 'warn' +str_to_string = 'warn' +string_add = 'warn' +string_lit_chars_any = 'warn' +string_slice = 'warn' +todo = 'warn' +# Enable `clippy::undocumented_unsafe_blocks` in case we ever change our stance on unsafe code. +undocumented_unsafe_blocks = 'warn' +unnecessary_self_imports = 'warn' +unwrap_used = 'warn' +verbose_file_reads = 'warn' + +# These are almost never helpful. +assertions_on_constants = { level = 'allow', priority = 1 } +map_unwrap_or = { level = 'allow', priority = 1 } +option_if_let_else = { level = 'allow', priority = 1 } +single_match_else = { level = 'allow', priority = 1 } +struct_field_names = { level = 'allow', priority = 1 } + +# These are almost never helpful and require boilerplate. +into_iter_without_iter = { level = 'allow', priority = 1 } +len_without_is_empty = { level = 'allow', priority = 1 } + +# `derivative::Derivative` and `fixed_hash::construct_fixed_hash!` generate code that triggers these. +# It is not just a bug in the macros. +# `clippy::expl_impl_clone_on_copy` produces false positives for types with type parameters. +# See . +# did not fix the issue. +# `clippy::incorrect_clone_impl_on_copy_type` does not have the same problem. +# `derivative` has open issues for 2 of the lints: +# - +# - +expl_impl_clone_on_copy = { level = 'allow', priority = 1 } +non_canonical_clone_impl = { level = 'allow', priority = 1 } +non_canonical_partial_ord_impl = { level = 'allow', priority = 1 } + +# `clippy::implicit_hasher` has next to no benefit and sometimes requires nonlocal changes to code. +implicit_hasher = { level = 'allow', priority = 1 } + +# `clippy::semicolon_if_nothing_returned` can lead to return values accidentally being left unused. +semicolon_if_nothing_returned = { level = 'allow', priority = 1 } + +# `clippy::significant_drop_in_scrutinee` produces mostly false positives. See: +# - +# - +# - +significant_drop_in_scrutinee = { level = 'allow', priority = 1 } + +# `clippy::significant_drop_tightening` often produces false positives. +# See . +significant_drop_tightening = { level = 'allow', priority = 1 } + +# Some functions in the codebase trigger `clippy::large_stack_frames`, but the lint does not +# report which ones, making it nearly useless. The lint does report which crates they are in, +# but only after checking other crates, which suggests it is triggered by generic functions. +large_stack_frames = { level = 'allow', priority = 1 } + +# This does not improve performance in any of our benchmarks. See discussions at: +# - +# - +large_types_passed_by_value = { level = 'allow', priority = 1 } + +missing_errors_doc = { level = 'allow', priority = 1 } +missing_panics_doc = { level = 'allow', priority = 1 } + +[workspace.lints.rustdoc] +private_intra_doc_links = 'allow' + [workspace.dependencies] +xmss = { path = "./xmss" } +env-config = { path = "./env-config" } chain = { path = "./chain" } containers = { path = "./containers" } fork_choice = { path = "./fork_choice" } networking = { path = "./networking" } validator = { path = "./validator" } + +anyhow = "1.0.100" +async-trait = "0.1" +clap = { version = "4", features = ["derive"] } +derive_more = "2.1.1" +discv5 = "0.10.2" +enr = { version = "0.13", features = ["k256"] } +eth_ssz = { package = "ethereum_ssz", version = "0.10.0" } +ethereum-types = "0.14" +futures = "0.3" +hex = "0.4.3" +k256 = "0.13" +lean-multisig = { git = "https://github.com/leanEthereum/leanMultisig", rev = "e4474138487eeb1ed7c2e1013674fe80ac9f3165" } +leansig = { git = "https://github.com/leanEthereum/leanSig", rev = "73bedc26ed961b110df7ac2e234dc11361a4bf25" } libp2p = { version = "0.56.0", default-features = false, features = [ 'dns', 'gossipsub', @@ -26,47 +243,51 @@ libp2p = { version = "0.56.0", default-features = false, features = [ 'tokio', 'yamux' ] } -alloy-consensus = "1.0.38" -alloy-primitives = "1.4.0" -anyhow = "1.0" +libp2p-identity = { version = "0.2", features = ["secp256k1"] } +libp2p-mplex = "0.39" +num-bigint = "0.4" +num-traits = "0.2" +parking_lot = "0.12" paste = "1.0.15" +pretty_assertions = "1.4" +rand = "0.9" +rand_chacha = "0.9" +rstest = "0.18" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" serde_yaml = "0.9" +sha2 = "0.10" snap = "1.1" -ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", branch = "develop" } -ssz_derive = { git = "https://github.com/grandinetech/grandine", package = "ssz_derive", branch = "develop" } +ssz = { git = "https://github.com/grandinetech/grandine", package = "ssz", rev = "64afdee3c6be79fceffb66933dcb69a943f3f1ae" } ssz-types = "0.3.0" +test-generator = "0.3.1" +tiny-keccak = "2.0.2" tokio = { version = "1.0", features = ["full"] } +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } tree-hash = "0.4.0" +try_from_iterator = { git = "https://github.com/grandinetech/grandine", package = "try_from_iterator", rev = "64afdee3c6be79fceffb66933dcb69a943f3f1ae" } typenum = "1.19" -sha2 = "0.10" -rand = "0.9" -test-generator = "0.3.1" - -[workspace.dev-dependencies] -rstest = "0.18.2" -pretty_assertions = "1.4.0" -serde_json = "1.0.107" +yamux = "0.12" +zeroize = "1.8" [package] name = "lean_client" version = "0.1.0" -edition = "2021" - -[features] -default = ["xmss-signing", "containers/xmss-verify"] -xmss-signing = ["validator/xmss-signing"] -xmss-verify = ["containers/xmss-verify"] +edition = { workspace = true } [dependencies] -chain = { path = "./chain" } -containers = { path = "./containers" } -fork-choice = { path = "./fork_choice" } -networking = { path = "./networking" } -validator = { path = "./validator" } -tokio = { version = "1.0", features = ["full"] } -clap = { version = "4", features = ["derive"] } -tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } -tracing = "0.1.41" -hex = "0.4" -libp2p-identity = { version = "0.2", features = ["secp256k1"] } +chain = { workspace = true } +clap = { workspace = true } +containers = { workspace = true } +ethereum-types = { workspace = true } +fork_choice = { workspace = true } +hex = { workspace = true } +libp2p-identity = { workspace = true } +networking = { workspace = true } +ssz = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +validator = { workspace = true } +xmss = { workspace = true } \ No newline at end of file diff --git a/lean_client/Makefile b/lean_client/Makefile index 6153df6..cf4dc05 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -2,7 +2,7 @@ # Commit of lean specification. Allows to specify which spec version to use, # when generating test vectors. Currently set to devnet-2 spec version. -LEAN_SPEC_COMMIT ?= c187aab89e0ecc6ce9c1fd9304fd708312ea7106 +LEAN_SPEC_COMMIT ?= 4edcf7bc9271e6a70ded8aff17710d68beac4266 # Docker image name. Used for both release (with publish) and local builds. DOCKER_REPO ?= sifrai/lean # Image tag. @@ -42,7 +42,7 @@ check-format: .PHONY: test test: - cargo test --workspace --all-features --no-fail-fast + cargo test --workspace --all-features --no-fail-fast --release .PHONY: generate-test-vectors generate-test-vectors: diff --git a/lean_client/chain/Cargo.toml b/lean_client/chain/Cargo.toml index 43fff26..fde964c 100644 --- a/lean_client/chain/Cargo.toml +++ b/lean_client/chain/Cargo.toml @@ -1,10 +1,5 @@ [package] name = "chain" -version = "0.1.0" -edition = "2021" - -[lib] -name = "chain" -path = "src/lib.rs" +edition = { workspace = true } [dependencies] diff --git a/lean_client/containers/Cargo.toml b/lean_client/containers/Cargo.toml index 282fc6b..bdb9c3f 100644 --- a/lean_client/containers/Cargo.toml +++ b/lean_client/containers/Cargo.toml @@ -1,36 +1,23 @@ [package] name = "containers" -version = "0.1.0" -edition = "2021" - -[features] -xmss-verify = [] -default = [] - -[lib] -name = "containers" -path = "src/lib.rs" +edition = { workspace = true } [dependencies] -env-config = { path = "../env-config", default-features = false } -ssz = { workspace = true } +anyhow = { workspace = true } +env-config = { workspace = true } +hex = { workspace = true } serde = { workspace = true } -ssz_derive = { workspace = true } -tracing = "0.1" -typenum = "1" -serde_json = "1.0" -serde_yaml = "0.9" -hex = "0.4.3" -sha2 = "0.10" -leansig = { git = "https://github.com/leanEthereum/leanSig", rev = "73bedc26ed961b110df7ac2e234dc11361a4bf25" } -lean-multisig = { git = "https://github.com/leanEthereum/leanMultisig", rev = "e4474138487eeb1ed7c2e1013674fe80ac9f3165" } -anyhow = "1.0.100" -alloy-primitives = "1.5.2" -# ethereum_ssz for lean-multisig types (aliased to avoid conflict with grandine ssz) -eth_ssz = { package = "ethereum_ssz", version = "0.10.0" } +serde_json = { workspace = true } +serde_yaml = { workspace = true } +sha2 = { workspace = true } +ssz = { workspace = true } +tracing = { workspace = true } +try_from_iterator = { workspace = true } +typenum = { workspace = true } +xmss = { workspace = true } [dev-dependencies] -rstest = "0.18" -pretty_assertions = "1.4" -serde_json = "1.0" +pretty_assertions = { workspace = true } +rstest = { workspace = true } +serde_json = { workspace = true } test-generator = { workspace = true } diff --git a/lean_client/containers/src/attestation.rs b/lean_client/containers/src/attestation.rs index fb45e0c..62b20b1 100644 --- a/lean_client/containers/src/attestation.rs +++ b/lean_client/containers/src/attestation.rs @@ -1,235 +1,72 @@ -use crate::{Checkpoint, Slot, Uint64}; -use anyhow::anyhow; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use ssz::BitList; -use ssz::ByteVector; -use ssz::{SszHash, H256}; -use ssz_derive::Ssz; +use ssz::{BitList, H256, PersistentList, Ssz, SszHash}; use std::collections::HashSet; -use typenum::{Prod, Sum, U100, U1024, U12, U31}; +use typenum::{U4096, Unsigned as _}; +use xmss::{AggregatedSignature, PublicKey, Signature}; -// Type-level number for 1 MiB (1048576 = 1024 * 1024) -type U1048576 = Prod; - -pub type U3100 = Prod; - -// Type-level number for 3112 bytes -pub type U3112 = Sum; - -// Type alias for Signature -pub type Signature = ByteVector; - -// Type-level number for 4096 (validator registry limit) -use typenum::U4096; +use crate::{Checkpoint, Slot, validator::ValidatorRegistryLimit}; /// List of validator attestations included in a block (without signatures). /// Limit is VALIDATOR_REGISTRY_LIMIT (4096). -pub type Attestations = ssz::PersistentList; - -pub type AggregatedAttestations = ssz::PersistentList; - -pub type AttestationSignatures = ssz::PersistentList; - -/// Legacy naive aggregated signature type (list of individual XMSS signatures). -/// Kept for backwards compatibility but no longer used in wire format. -pub type NaiveAggregatedSignature = ssz::PersistentList; - -/// Aggregated signature proof from lean-multisig zkVM. -/// -/// This is a variable-length byte list (up to 1 MiB) containing the serialized -/// proof bytes from `xmss_aggregate_signatures()`. The `#[ssz(transparent)]` -/// attribute makes this type serialize directly as a ByteList for SSZ wire format. -#[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] -#[ssz(transparent)] -pub struct MultisigAggregatedSignature( - /// The serialized zkVM proof bytes from lean-multisig aggregation. - #[serde(with = "crate::serde_helpers::byte_list")] - pub ssz::ByteList, -); - -impl MultisigAggregatedSignature { - /// Create a new MultisigAggregatedSignature from proof bytes. - pub fn new(proof: Vec) -> Result { - ssz::ByteList::try_from(proof) - .map(Self) - .map_err(|_| AggregationError::AggregationFailed) - } - - /// Get the proof bytes. - pub fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() - } - - /// Check if the signature is empty (no proof). - pub fn is_empty(&self) -> bool { - self.0.as_bytes().is_empty() - } - - /// Aggregate individual XMSS signatures into a single proof. - /// - /// Uses lean-multisig zkVM to combine multiple signatures into a compact proof. - /// - /// # Arguments - /// * `public_keys` - Slice of validator public keys - /// * `signatures` - Individual XMSS signatures to aggregate - /// * `message` - The 32-byte message that was signed - /// * `epoch` - The epoch/slot in which signatures were created - /// - /// # Returns - /// Aggregated signature proof, or error if aggregation fails. - pub fn aggregate( - public_keys: &[crate::public_key::PublicKey], - signatures: &[Signature], - message: &[u8; 32], - epoch: u32, - ) -> Result { - if public_keys.is_empty() { - return Err(AggregationError::EmptyInput); - } - if public_keys.len() != signatures.len() { - return Err(AggregationError::MismatchedLengths); - } - - // Convert to lean-multisig types - let lean_pks: Vec<_> = public_keys - .iter() - .map(|pk| pk.as_lean_sig()) - .collect::, _>>() - .map_err(|_| AggregationError::AggregationFailed)?; - - let lean_sigs: Vec<_> = signatures - .iter() - .map(|sig| { - // Convert ByteVector to crate::signature::Signature then to lean-sig - let sig_struct = crate::signature::Signature::from(sig.as_bytes()); - sig_struct.as_lean_sig() - }) - .collect::, _>>() - .map_err(|_| AggregationError::AggregationFailed)?; - - let aggregate_sig = - lean_multisig::xmss_aggregate_signatures(&lean_pks, &lean_sigs, message, epoch) - .map_err(|_| AggregationError::AggregationFailed)?; - - // Serialize the aggregate signature using ethereum_ssz (aliased as eth_ssz) - use eth_ssz::Encode; - let proof_bytes = aggregate_sig.as_ssz_bytes(); - Self::new(proof_bytes) - } - - /// Verify the aggregated signature proof against the given public keys and message. - /// - /// Uses lean-multisig zkVM to verify that the aggregated proof is valid - /// for all the given public keys signing the same message at the given epoch. - /// - /// # Returns - /// `Ok(())` if the proof is valid, `Err` with the proof error otherwise. - pub fn verify( - &self, - public_keys: &[crate::public_key::PublicKey], - message: &[u8; 32], - epoch: u32, - ) -> Result<(), AggregationError> { - // Use ethereum_ssz (aliased as eth_ssz) for decoding - use eth_ssz::Decode; - - // Decode the aggregated signature from SSZ bytes - let aggregate_sig = - lean_multisig::Devnet2XmssAggregateSignature::from_ssz_bytes(self.0.as_bytes()) - .map_err(|_| AggregationError::VerificationFailed)?; - - // Convert public keys to lean-multisig format - let lean_pks: Vec<_> = public_keys - .iter() - .map(|pk| pk.as_lean_sig()) - .collect::, _>>() - .map_err(|_| AggregationError::VerificationFailed)?; - - lean_multisig::xmss_verify_aggregated_signatures(&lean_pks, message, &aggregate_sig, epoch) - .map_err(|_| AggregationError::VerificationFailed) - } - - /// Verify the aggregated payload against validators and message. - /// - /// This is a convenience method that extracts public keys from validators. - /// - /// # Arguments - /// * `validators` - Slice of validator references to extract public keys from - /// * `message` - 32-byte message (typically attestation data root) - /// * `epoch` - Epoch/slot for proof verification - /// - /// # Returns - /// `Ok(())` if verification succeeds, `Err` otherwise. - pub fn verify_aggregated_payload( - &self, - validators: &[&crate::validator::Validator], - message: &[u8; 32], - epoch: u32, - ) -> Result<(), AggregationError> { - // Extract public keys from validators - let public_keys: Vec<_> = validators.iter().map(|v| v.pubkey).collect(); +pub type Attestations = PersistentList; - // Call verify with extracted keys - self.verify(&public_keys, message, epoch) - } -} +pub type AggregatedAttestations = PersistentList; -/// Error types for signature aggregation operations. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AggregationError { - /// No signatures provided for aggregation. - EmptyInput, - /// Public keys and signatures arrays have different lengths. - MismatchedLengths, - /// Aggregation failed in lean-multisig. - AggregationFailed, - /// Verification of aggregated proof failed. - VerificationFailed, -} +pub type AttestationSignatures = PersistentList; /// Aggregated signature proof with participant tracking. /// /// This type combines the participant bitfield with the proof bytes, /// matches Python's `AggregatedSignatureProof` container structure. /// Used in `aggregated_payloads` to track which validators are covered by each proof. -#[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AggregatedSignatureProof { /// Bitfield indicating which validators' signatures are included. - pub participants: AggregationBits, + participants: AggregationBits, /// The raw aggregated proof bytes from lean-multisig. - pub proof_data: MultisigAggregatedSignature, + pub proof_data: AggregatedSignature, } impl AggregatedSignatureProof { - /// Create a new AggregatedSignatureProof. - pub fn new(participants: AggregationBits, proof_data: MultisigAggregatedSignature) -> Self { - Self { + pub fn aggregate( + participants: AggregationBits, + public_keys: impl IntoIterator, + signatures: impl IntoIterator, + message: H256, + epoch: u32, + ) -> Result { + Ok(Self { participants, - proof_data, - } - } - - pub fn from_aggregation(participant_ids: &[u64], proof: MultisigAggregatedSignature) -> Self { - Self { - participants: AggregationBits::from_validator_indices(participant_ids), - proof_data: proof, - } + proof_data: AggregatedSignature::aggregate(public_keys, signatures, message, epoch)?, + }) } /// Get the validator indices covered by this proof. pub fn get_participant_indices(&self) -> Vec { self.participants.to_validator_indices() } + + pub fn verify( + &self, + public_keys: impl IntoIterator, + message: H256, + epoch: u32, + ) -> Result<()> { + self.proof_data.verify(public_keys, message, epoch) + } } /// Bitlist representing validator participation in an attestation. /// Limit is VALIDATOR_REGISTRY_LIMIT (4096). -#[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] -pub struct AggregationBits(#[serde(with = "crate::serde_helpers::bitlist")] pub BitList); +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] +pub struct AggregationBits( + #[serde(with = "crate::serde_helpers::bitlist")] pub BitList, +); impl AggregationBits { - pub const LIMIT: u64 = 4096; + pub const LIMIT: u64 = ValidatorRegistryLimit::U64; pub fn from_validator_indices(indices: &[u64]) -> Self { assert!( @@ -278,7 +115,9 @@ impl AggregationBits { pub type AggregatedSignatures = ssz::PersistentList; /// Attestation content describing the validator's observed chain view. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +/// +/// todo(containers): default implementation doesn't make sense here +#[derive(Clone, Debug, Ssz, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct AttestationData { /// The slot for which the attestation is made. pub slot: Slot, @@ -290,27 +129,19 @@ pub struct AttestationData { pub source: Checkpoint, } -impl AttestationData { - /// Compute the data root bytes for signature lookup. - /// This is the hash tree root of the attestation data. - pub fn data_root_bytes(&self) -> crate::Bytes32 { - crate::Bytes32(ssz::SszHash::hash_tree_root(self)) - } -} - /// Key for looking up individual validator signatures. /// Used to index signature caches by (validator, attestation_data_root) pairs. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct SignatureKey { /// The validator who produced the signature. pub validator_id: u64, /// The hash of the signed attestation data. - pub data_root: crate::Bytes32, + pub data_root: H256, } impl SignatureKey { /// Create a new signature key. - pub fn new(validator_id: u64, data_root: crate::Bytes32) -> Self { + pub fn new(validator_id: u64, data_root: H256) -> Self { Self { validator_id, data_root, @@ -319,17 +150,19 @@ impl SignatureKey { } /// Validator specific attestation wrapping shared attestation data. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +/// +/// todo(containers): default implementation doesn't make sense here +#[derive(Clone, Debug, Ssz, Default, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Attestation { /// The index of the validator making the attestation. - pub validator_id: Uint64, + pub validator_id: u64, /// The attestation data produced by the validator. pub data: AttestationData, } /// Validator attestation bundled with its signature. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz)] pub struct SignedAttestation { pub validator_id: u64, pub message: AttestationData, @@ -337,7 +170,7 @@ pub struct SignedAttestation { } /// Aggregated attestation consisting of participation bits and message. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AggregatedAttestation { /// Bitfield indicating which validators participated in the aggregation. @@ -359,10 +192,10 @@ impl AggregatedAttestation { .iter_mut() .find(|(data, _)| *data == attestation.data) { - validator_ids.push(attestation.validator_id.0); + validator_ids.push(attestation.validator_id); } else { // Create a new group - groups.push((attestation.data.clone(), vec![attestation.validator_id.0])); + groups.push((attestation.data.clone(), vec![attestation.validator_id])); } } @@ -389,7 +222,7 @@ impl AggregatedAttestation { } /// Aggregated attestation bundled with aggregated signatures. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz)] pub struct SignedAggregatedAttestation { /// Aggregated attestation data. pub message: AggregatedAttestation, diff --git a/lean_client/containers/src/block.rs b/lean_client/containers/src/block.rs index 141aebc..95e2cb0 100644 --- a/lean_client/containers/src/block.rs +++ b/lean_client/containers/src/block.rs @@ -1,9 +1,8 @@ -use crate::{ - Attestation, Bytes32, MultisigAggregatedSignature, Signature, Slot, State, ValidatorIndex, -}; +use crate::{Attestation, Slot, State}; +use anyhow::{Context, Result, anyhow, ensure}; use serde::{Deserialize, Serialize}; -use ssz::SszHash; -use ssz_derive::Ssz; +use ssz::{H256, Ssz, SszHash}; +use xmss::Signature; use crate::attestation::{AggregatedAttestations, AttestationSignatures}; @@ -11,34 +10,35 @@ use crate::attestation::{AggregatedAttestations, AttestationSignatures}; /// /// Attestations are stored WITHOUT signatures. Signatures are aggregated /// separately in BlockSignatures to match the spec architecture. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +// todo(containers): default implementation doesn't make sense here. +#[derive(Clone, Debug, Ssz, Serialize, Deserialize, Default)] pub struct BlockBody { #[serde(with = "crate::serde_helpers::aggregated_attestations")] pub attestations: AggregatedAttestations, } -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BlockHeader { pub slot: Slot, - pub proposer_index: ValidatorIndex, - pub parent_root: Bytes32, - pub state_root: Bytes32, - pub body_root: Bytes32, + pub proposer_index: u64, + pub parent_root: H256, + pub state_root: H256, + pub body_root: H256, } -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Block { pub slot: Slot, - pub proposer_index: ValidatorIndex, - pub parent_root: Bytes32, - pub state_root: Bytes32, + pub proposer_index: u64, + pub parent_root: H256, + pub state_root: H256, pub body: BlockBody, } /// Bundle containing a block and the proposer's attestation. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BlockWithAttestation { /// The proposed block message. @@ -47,17 +47,17 @@ pub struct BlockWithAttestation { pub proposer_attestation: Attestation, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Ssz, Deserialize, Default)] +// todo(containers): default implementation doesn't make sense here +#[derive(Debug, Clone, Ssz, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct BlockSignatures { #[serde(with = "crate::serde_helpers::attestation_signatures")] pub attestation_signatures: AttestationSignatures, - #[serde(with = "crate::serde_helpers::signature")] pub proposer_signature: Signature, } /// Envelope carrying a block, an attestation from proposer, and aggregated signatures. -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SignedBlockWithAttestation { /// The block plus an attestation from proposer being signed. @@ -69,23 +69,12 @@ pub struct SignedBlockWithAttestation { } /// Legacy signed block structure (kept for backwards compatibility). -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Ssz)] pub struct SignedBlock { pub message: Block, pub signature: Signature, } -/// Compute the SSZ hash tree root for any type implementing `SszHash`. -pub fn hash_tree_root(value: &T) -> Bytes32 { - let h = value.hash_tree_root(); - Bytes32(h) -} - -/// Compute the canonical block root for a Block. -pub fn compute_block_root(block: &Block) -> Bytes32 { - Bytes32(block.hash_tree_root()) -} - impl SignedBlockWithAttestation { /// Verify all XMSS signatures in this signed block. /// @@ -132,29 +121,28 @@ impl SignedBlockWithAttestation { /// all participating validators. /// /// Returns `Ok(())` if all signatures are valid, or an error describing the failure. - pub fn verify_signatures(&self, parent_state: State) -> Result<(), String> { + pub fn verify_signatures(&self, parent_state: State) -> Result<()> { // Unpack the signed block components let block = &self.message.block; let signatures = &self.signature; - let aggregated_attestations = block.body.attestations.clone(); - let attestation_signatures = signatures.attestation_signatures.clone(); + let aggregated_attestations = &block.body.attestations; + let attestation_signatures = &signatures.attestation_signatures; // Verify signature count matches aggregated attestation count - if aggregated_attestations.len_u64() != attestation_signatures.len_u64() { - return Err(format!( - "Attestation signature count mismatch: {} attestations vs {} signatures", - aggregated_attestations.len_u64(), - attestation_signatures.len_u64() - )); - } + ensure!( + aggregated_attestations.len_u64() == attestation_signatures.len_u64(), + "attestation signature count mismatch: {} attestations vs {} signatures", + aggregated_attestations.len_u64(), + attestation_signatures.len_u64() + ); let validators = &parent_state.validators; let num_validators = validators.len_u64(); // Verify each aggregated attestation's zkVM proof - for (aggregated_attestation, _aggregated_signature_proof) in (&aggregated_attestations) + for (aggregated_attestation, aggregated_signature) in aggregated_attestations .into_iter() - .zip((&attestation_signatures).into_iter()) + .zip(attestation_signatures.into_iter()) { let validator_ids = aggregated_attestation .aggregation_bits @@ -162,101 +150,63 @@ impl SignedBlockWithAttestation { // Ensure all validators exist in the active set for validator_id in &validator_ids { - if *validator_id >= num_validators { - return Err(format!( - "Validator index {} out of range (max {})", - validator_id, num_validators - )); - } + ensure!( + *validator_id < num_validators, + "validator index {validator_id} out of range (max {num_validators})" + ); } - let attestation_data_root: [u8; 32] = - hash_tree_root(&aggregated_attestation.data).0.into(); + let attestation_data_root = aggregated_attestation.data.hash_tree_root(); // Collect validators, returning error if any not found - let mut collected_validators = Vec::with_capacity(validator_ids.len()); - for vid in &validator_ids { - let validator = validators - .get(*vid) - .map_err(|_| format!("Validator {} not found in state", vid))?; - collected_validators.push(validator); - } + let public_keys = validator_ids + .into_iter() + .map(|id| { + validators + .get(id) + .map(|validator| validator.pubkey.clone()) + .map_err(Into::into) + }) + .collect::>>()?; // Verify the lean-multisig aggregated proof for this attestation // // The proof verifies that all validators in aggregation_bits signed // the same attestation_data_root at the given epoch (slot). - _aggregated_signature_proof - .proof_data - .verify_aggregated_payload( - &collected_validators, - &attestation_data_root, + aggregated_signature + .verify( + public_keys, + attestation_data_root, aggregated_attestation.data.slot.0 as u32, ) - .map_err(|e| { - format!( - "Attestation aggregated signature verification failed: {:?}", - e - ) - })?; + .context("attestation aggregated signature verification failed")?; } // Verify the proposer attestation signature (outside the attestation loop) let proposer_attestation = &self.message.proposer_attestation; let proposer_signature = &signatures.proposer_signature; - if proposer_attestation.validator_id.0 >= num_validators { - return Err(format!( - "Proposer index {} out of range (max {})", - proposer_attestation.validator_id.0, num_validators - )); - } + ensure!( + proposer_attestation.validator_id < num_validators, + "proposer index {} out of range (max {num_validators})", + proposer_attestation.validator_id + ); let proposer = validators - .get(proposer_attestation.validator_id.0) - .map_err(|_| { - format!( - "Proposer {} not found in state", - proposer_attestation.validator_id.0 - ) - })?; - - let proposer_root: [u8; 32] = hash_tree_root(&proposer_attestation.data).0.into(); - if !verify_xmss_signature( - proposer.pubkey, - proposer_attestation.data.slot, - &proposer_root, - proposer_signature, - ) { - return Err("Proposer attestation signature verification failed".to_string()); - } + .get(proposer_attestation.validator_id) + .context(format!( + "proposer {} not found in state", + proposer_attestation.validator_id + ))?; + + proposer_signature + .verify( + &proposer.pubkey, + proposer_attestation.data.slot.0 as u32, + proposer_attestation.data.hash_tree_root(), + ) + .context("Proposer signature verification failed")?; Ok(()) } } - -#[cfg(feature = "xmss-verify")] -pub fn verify_xmss_signature( - public_key: crate::public_key::PublicKey, - slot: Slot, - message_bytes: &[u8; 32], - signature: &Signature, -) -> bool { - let epoch = slot.0 as u32; - - // Create Signature from the raw bytes - let sig = crate::signature::Signature::from(signature.as_bytes()); - - sig.verify(&public_key, epoch, message_bytes) - .unwrap_or(false) -} - -#[cfg(not(feature = "xmss-verify"))] -pub fn verify_xmss_signature( - _public_key: crate::public_key::PublicKey, - _slot: Slot, - _message_bytes: &[u8; 32], - _signature: &Signature, -) -> bool { - true -} diff --git a/lean_client/containers/src/checkpoint.rs b/lean_client/containers/src/checkpoint.rs index 1b36f31..d737456 100644 --- a/lean_client/containers/src/checkpoint.rs +++ b/lean_client/containers/src/checkpoint.rs @@ -1,6 +1,6 @@ -use crate::{Bytes32, Slot}; +use crate::Slot; use serde::{Deserialize, Serialize}; -use ssz_derive::Ssz; +use ssz::{H256, Ssz}; /// Represents a checkpoint in the chain's history. /// @@ -10,39 +10,7 @@ use ssz_derive::Ssz; #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct Checkpoint { /// The root hash of the checkpoint's block. - pub root: Bytes32, + pub root: H256, /// The slot number of the checkpoint's block. pub slot: Slot, } - -impl Checkpoint { - /// Return a default checkpoint with zero root and slot 0. - pub fn default_checkpoint() -> Self { - Self { - root: Bytes32(ssz::H256::zero()), - slot: Slot(0), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_default_checkpoint() { - let checkpoint = Checkpoint::default_checkpoint(); - assert_eq!(checkpoint.root, Bytes32(ssz::H256::zero())); - assert_eq!(checkpoint.slot, Slot(0)); - } - - #[test] - fn test_checkpoint_equality() { - let cp1 = Checkpoint::default_checkpoint(); - let cp2 = Checkpoint { - root: Bytes32(ssz::H256::zero()), - slot: Slot(0), - }; - assert_eq!(cp1, cp2); - } -} diff --git a/lean_client/containers/src/config.rs b/lean_client/containers/src/config.rs index fed2b7e..59cd838 100644 --- a/lean_client/containers/src/config.rs +++ b/lean_client/containers/src/config.rs @@ -1,8 +1,6 @@ use serde::{Deserialize, Serialize}; -use ssz_derive::Ssz; -use std::fs::File; -use std::io::BufReader; -use std::path::Path; +use ssz::Ssz; +use std::{fs::File, io::BufReader, path::Path}; #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/lean_client/containers/src/lib.rs b/lean_client/containers/src/lib.rs index 0125a08..cd1a6c0 100644 --- a/lean_client/containers/src/lib.rs +++ b/lean_client/containers/src/lib.rs @@ -1,35 +1,27 @@ -pub mod attestation; -pub mod block; -pub mod checkpoint; -pub mod config; -pub mod public_key; -pub mod serde_helpers; -pub mod signature; -pub mod slot; -pub mod state; -pub mod status; -pub mod types; -pub mod validator; +mod attestation; +mod block; +mod checkpoint; +mod config; +mod serde_helpers; +mod slot; +mod state; +mod status; +mod validator; pub use attestation::{ - AggregatedAttestation, AggregatedSignatures, AggregationBits, Attestation, AttestationData, - Attestations, Signature, SignatureKey, SignedAggregatedAttestation, SignedAttestation, + AggregatedAttestation, AggregatedSignatureProof, AggregatedSignatures, AggregationBits, + Attestation, AttestationData, Attestations, SignatureKey, SignedAggregatedAttestation, + SignedAttestation, }; - -pub use attestation::{AggregatedSignatureProof, MultisigAggregatedSignature}; pub use block::{ - Block, BlockBody, BlockHeader, BlockWithAttestation, SignedBlock, SignedBlockWithAttestation, + Block, BlockBody, BlockHeader, BlockSignatures, BlockWithAttestation, SignedBlock, + SignedBlockWithAttestation, }; pub use checkpoint::Checkpoint; pub use config::{Config, GenesisConfig}; pub use slot::Slot; -pub use state::State; -pub use status::Status; -pub use types::{ - Bytes32, HistoricalBlockHashes, JustificationRoots, JustificationsValidators, JustifiedSlots, - Uint64, ValidatorIndex, Validators, +pub use state::{ + HistoricalBlockHashes, JustificationRoots, JustificationValidators, JustifiedSlots, State, }; - -pub use types::Bytes32 as Root; -// Re-export grandine ssz so tests can reference it if needed -pub use ssz; +pub use status::Status; +pub use validator::{Validator, Validators}; diff --git a/lean_client/containers/src/public_key.rs b/lean_client/containers/src/public_key.rs deleted file mode 100644 index 529b00c..0000000 --- a/lean_client/containers/src/public_key.rs +++ /dev/null @@ -1,215 +0,0 @@ -use alloy_primitives::{hex::{self, ToHexExt}}; -use anyhow::{anyhow}; -use leansig::{serialization::Serializable, signature::SignatureScheme}; -use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8; -use serde::{Deserialize, Deserializer, Serialize}; -use ssz::{SszSize, SszRead, SszWrite, SszHash, Size, WriteError, ReadError, H256}; - -const PUBLIC_KEY_SIZE: usize = 52; -pub type LeanSigPublicKey = - ::PublicKey; - -// This is a wrapper class for storing public keys, implementation based on Ream client -#[derive(Debug, PartialEq, Clone, Eq, Hash, Copy)] -pub struct PublicKey { - pub inner: [u8; PUBLIC_KEY_SIZE], -} - -impl From<&[u8]> for PublicKey { - fn from(value: &[u8]) -> Self { - // Handle potential length panics or ensure slice is correct size - let mut inner = [0u8; PUBLIC_KEY_SIZE]; - let len = value.len().min(PUBLIC_KEY_SIZE); - inner[..len].copy_from_slice(&value[..len]); - Self { inner } - } -} - -impl Default for PublicKey { - fn default() -> Self { - Self { - inner: [0u8; PUBLIC_KEY_SIZE], - } - } -} - -impl SszSize for PublicKey { - const SIZE: Size = Size::Fixed { - size: PUBLIC_KEY_SIZE, - }; -} - -// 2. Define how to write (Serialize) -impl SszWrite for PublicKey { - fn write_fixed(&self, bytes: &mut [u8]) { - // Write the 52 bytes of the public key - bytes[..PUBLIC_KEY_SIZE].copy_from_slice(&self.inner); - } - - fn write_variable(&self, _bytes: &mut Vec) -> Result<(), WriteError> { - // PublicKey is fixed-size, this should not be called - panic!("PublicKey is fixed-size, write_variable should not be called"); - } - - fn to_ssz(&self) -> Result, WriteError> { - let mut bytes = vec![0u8; PUBLIC_KEY_SIZE]; - self.write_fixed(&mut bytes); - Ok(bytes) - } -} - -impl SszRead for PublicKey { - fn from_ssz_unchecked(_context: &C, bytes: &[u8]) -> Result { - // For a fixed-size struct, we must ensure we have exactly - // the number of bytes required by our SszSize implementation. - if bytes.len() != PUBLIC_KEY_SIZE { - return Err(ReadError::FixedSizeMismatch { - expected: PUBLIC_KEY_SIZE, - actual: bytes.len(), - }); - } - - let mut inner = [0u8; PUBLIC_KEY_SIZE]; - inner.copy_from_slice(bytes); - - Ok(Self { inner }) - } - fn from_ssz(context: &C, bytes: impl AsRef<[u8]>) -> Result { - let bytes_ref = bytes.as_ref(); - - // SSZ fixed-size validation - if bytes_ref.len() != PUBLIC_KEY_SIZE { - return Err(ReadError::FixedSizeMismatch { - expected: PUBLIC_KEY_SIZE, - actual: bytes_ref.len(), - }); - } - - Self::from_ssz_unchecked(context, bytes_ref) - } -} - -impl SszHash for PublicKey { - type PackingFactor = typenum::U1; - - fn hash_tree_root(&self) -> H256 { - // SSZ hash_tree_root for fixed-size types > 32 bytes: - // 1. Split into 32-byte chunks - // 2. Pad last chunk with zeros if needed - // 3. Merkleize the chunks - use sha2::{Digest, Sha256}; - - // For 52 bytes: 2 chunks (32 + 20 bytes, second chunk padded to 32) - let mut chunk1 = [0u8; 32]; - let mut chunk2 = [0u8; 32]; - - chunk1.copy_from_slice(&self.inner[0..32]); - chunk2[..20].copy_from_slice(&self.inner[32..52]); - // Remaining 12 bytes of chunk2 are already zeros (padding) - - // Merkleize: hash(chunk1 || chunk2) - let mut hasher = Sha256::new(); - hasher.update(&chunk1); - hasher.update(&chunk2); - let result = hasher.finalize(); - - H256::from_slice(&result) - } -} - -impl PublicKey { - pub fn new(inner: [u8; PUBLIC_KEY_SIZE]) -> Self { - Self { inner } - } - - pub fn from_lean_sig(public_key: LeanSigPublicKey) -> Result { - let bytes = public_key.to_bytes(); - // Ensure we fit into 52 bytes - if bytes.len() != PUBLIC_KEY_SIZE { - return Err(anyhow!( - "LeanSigPublicKey length mismatch: expected 52, got {}", - bytes.len() - )); - } - let mut inner = [0u8; PUBLIC_KEY_SIZE]; - inner.copy_from_slice(&bytes); - Ok(Self { inner }) - } - - pub fn as_lean_sig(&self) -> anyhow::Result { - LeanSigPublicKey::from_bytes(&self.inner) - .map_err(|err| anyhow!("Failed to decode LeanSigPublicKey from SSZ: {err:?}")) - } - - pub fn from_hex>(s: S) -> anyhow::Result { - let s = s.as_ref(); - - // Allow optional 0x prefix - let s = s.strip_prefix("0x").unwrap_or(s); - - let bytes = hex::decode(s).map_err(|e| anyhow!("Invalid hex public key: {e}"))?; - - if bytes.len() != 52 { - return Err(anyhow!( - "PublicKey hex length mismatch: expected 52 bytes, got {}", - bytes.len() - )); - } - - // Validate structure via LeanSig - let lean_pk = LeanSigPublicKey::from_bytes(&bytes) - .map_err(|e| anyhow!("Invalid XMSS public key encoding: {e:?}"))?; - - Self::from_lean_sig(lean_pk) - } - - pub fn debug_roundtrip(&self) -> anyhow::Result<()> { - let pk = self.as_lean_sig()?; - let re = pk.to_bytes(); - - anyhow::ensure!( - re.as_slice() == self.inner.as_slice(), - "PublicKey roundtrip mismatch: decoded->encoded bytes differ" - ); - - Ok(()) - } - - pub fn fingerprint_hex(&self) -> String { - use alloy_primitives::hex::ToHexExt; - let take = self.inner.len().min(12); - format!("0x{}", ToHexExt::encode_hex(&self.inner[..take].iter())) - } -} - -impl Serialize for PublicKey { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&format!( - "0x{}", - self.as_lean_sig() - .map_err(serde::ser::Error::custom)? - .to_bytes() - .encode_hex() - )) - } -} - -impl<'de> Deserialize<'de> for PublicKey { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let result: String = Deserialize::deserialize(deserializer)?; - let result = hex::decode(&result).map_err(serde::de::Error::custom)?; - - Self::from_lean_sig( - LeanSigPublicKey::from_bytes(&result) - .map_err(|err| anyhow!("Convert to error, with error trait implemented {err:?}")) - .map_err(serde::de::Error::custom)?, - ) - .map_err(serde::de::Error::custom) - } -} diff --git a/lean_client/containers/src/serde_helpers.rs b/lean_client/containers/src/serde_helpers.rs index 3f3aa86..730ff5f 100644 --- a/lean_client/containers/src/serde_helpers.rs +++ b/lean_client/containers/src/serde_helpers.rs @@ -100,155 +100,12 @@ pub mod bitlist { } } -/// Special deserializer for Signature that handles structured XMSS format from test vectors -/// Signatures in test vectors are structured with {path, rho, hashes} instead of hex bytes -pub mod signature { - use super::*; - use crate::Signature; - use serde_json::Value; - - /// Structured XMSS signature format from test vectors - #[derive(Deserialize)] - struct XmssSignature { - path: XmssPath, - rho: DataWrapper>, - hashes: DataWrapper>>>, - } - - #[derive(Deserialize)] - struct XmssPath { - siblings: DataWrapper>>>, - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - - // First, try to parse as a JSON value to inspect the structure - let value = Value::deserialize(deserializer)?; - - // Check if it's a hex string (normal format) - if let Value::String(hex_str) = value { - let hex_str = hex_str.trim_start_matches("0x"); - let bytes = hex::decode(hex_str) - .map_err(|e| D::Error::custom(format!("Invalid hex string: {}", e)))?; - - return Signature::try_from(bytes.as_slice()) - .map_err(|_| D::Error::custom("Invalid signature length")); - } - - // Otherwise, parse as structured XMSS signature - let xmss_sig: XmssSignature = serde_json::from_value(value.clone()) - .map_err(|e| D::Error::custom(format!("Failed to parse XMSS signature: {}", e)))?; - - println!( - "Parsed XMSS Signature | siblings: {:?}", - xmss_sig.path.siblings.data.len() - ); - println!("Parsed XMSS Signature | rho: {:?}", xmss_sig.rho.data.len()); - println!( - "Parsed XMSS Signature | hashes: {:?}", - xmss_sig.hashes.data.len() - ); - - // --- STEP 1: PREPARE DATA BUFFERS --- - - // 1. Serialize Rho (Fixed length) - // RAND_LEN_FE = 7, assuming u32 elements -> 28 bytes - let mut rho_bytes = Vec::new(); - for val in &xmss_sig.rho.data { - rho_bytes.extend_from_slice(&val.to_le_bytes()); - } - let rho_len = rho_bytes.len(); // Should be 28 (7 * 4) - - // 2. Serialize Path/Siblings (Variable length) - let mut path_bytes = Vec::new(); - // Prepend 4 bytes (containing 4) as an offset which would come with real SSZ serialization - let inner_offset: u32 = 4; - path_bytes.extend_from_slice(&inner_offset.to_le_bytes()); // [04 00 00 00] - for sibling in &xmss_sig.path.siblings.data { - for val in &sibling.data { - path_bytes.extend_from_slice(&val.to_le_bytes()); - } - } - - // 3. Serialize Hashes (Variable length) - let mut hashes_bytes = Vec::new(); - for hash in &xmss_sig.hashes.data { - for val in &hash.data { - hashes_bytes.extend_from_slice(&val.to_le_bytes()); - } - } - - // --- STEP 2: CALCULATE OFFSETS --- - - // The fixed part contains: - // 1. Path Offset (4 bytes) - // 2. Rho Data (rho_len bytes) - // 3. Hashes Offset (4 bytes) - let fixed_part_size = 4 + rho_len + 4; - - // Offset to 'path' starts immediately after the fixed part - let offset_path = fixed_part_size as u32; - - // Offset to 'hashes' starts after 'path' data - let offset_hashes = offset_path + (path_bytes.len() as u32); - - // --- STEP 3: CONSTRUCT FINAL SSZ BYTES --- - - // Print all offsets and lengths for debugging - println!( - "SSZ Offsets | offset_path: {} | offset_hashes: {}", - offset_path, offset_hashes - ); - println!( - "SSZ Lengths | rho_len: {} | path_len: {} | hashes_len: {}", - rho_len, - path_bytes.len(), - hashes_bytes.len() - ); - - let mut ssz_bytes = Vec::new(); - - // 1. Write Offset to Path (u32, Little Endian) - ssz_bytes.extend_from_slice(&offset_path.to_le_bytes()); - - // 2. Write Rho Data (Fixed) - ssz_bytes.extend_from_slice(&rho_bytes); - - // 3. Write Offset to Hashes (u32, Little Endian) - ssz_bytes.extend_from_slice(&offset_hashes.to_le_bytes()); - - // 4. Write Path Data (Variable) - ssz_bytes.extend_from_slice(&path_bytes); - - // 5. Write Hashes Data (Variable) - ssz_bytes.extend_from_slice(&hashes_bytes); - - println!("Total SSZ Bytes Length: {}", ssz_bytes.len()); - - Signature::try_from(ssz_bytes.as_slice()) - .map_err(|_| D::Error::custom("Failed to create signature")) - } - - pub fn serialize(value: &Signature, serializer: S) -> Result - where - S: Serializer, - { - // Serialize as hex string - let hex_str = format!("0x{}", hex::encode(value.as_bytes())); - hex_str.serialize(serializer) - } -} - /// Custom deserializer for AttestationSignatures that handles the {"data": [sig, ...]} format /// where each signature can be either hex string or structured XMSS format pub mod attestation_signatures { use super::*; - use crate::attestation::AttestationSignatures; use crate::AggregatedSignatureProof; + use crate::attestation::AttestationSignatures; use serde::de::Error; use ssz::PersistentList; use typenum::U4096; @@ -346,8 +203,8 @@ pub mod byte_list { /// where each signature can be either hex string or structured XMSS format pub mod aggregated_attestations { use super::*; - use crate::attestation::AggregatedAttestations; use crate::AggregatedAttestation; + use crate::attestation::AggregatedAttestations; use serde::de::Error; use ssz::PersistentList; use typenum::U4096; diff --git a/lean_client/containers/src/signature.rs b/lean_client/containers/src/signature.rs deleted file mode 100644 index ab39873..0000000 --- a/lean_client/containers/src/signature.rs +++ /dev/null @@ -1,121 +0,0 @@ -use alloy_primitives::hex::ToHexExt; -use anyhow::anyhow; -use leansig::{MESSAGE_LENGTH, serialization::Serializable, signature::SignatureScheme}; -use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8; -use serde::{Deserialize, Deserializer, Serialize}; -use crate::public_key::{PublicKey}; - -const SIGNATURE_SIZE: usize = 3112; - -type LeanSigSignature = ::Signature; - -/// Wrapper around a fixed-size serialized hash-based signature. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct Signature { - pub inner: [u8; SIGNATURE_SIZE], -} - -impl From<&[u8]> for Signature { - fn from(value: &[u8]) -> Self { - // Handle potential length panics or ensure slice is correct size - let mut inner = [0u8; SIGNATURE_SIZE]; - let len = value.len().min(SIGNATURE_SIZE); - inner[..len].copy_from_slice(&value[..len]); - Self { inner } - } -} - -impl Signature { - pub fn new(inner: [u8; SIGNATURE_SIZE]) -> Self { - Self { inner } - } - - pub fn from_lean_sig(signature: LeanSigSignature) -> Result { - let bytes = signature.to_bytes(); - // Ensure we fit into 3112 bytes - if bytes.len() != 3112 { - return Err(anyhow!( - "LeanSigSignature length mismatch: expected 3112, got {}", - bytes.len() - )); - } - let mut inner = [0u8; SIGNATURE_SIZE]; - inner.copy_from_slice(&bytes); - Ok(Self { inner }) - } - - pub fn as_lean_sig(&self) -> anyhow::Result { - println!("Converting Signature to LeanSigSignature..."); - LeanSigSignature::from_bytes(&self.inner) - .map_err(|err| anyhow!("Failed to decode LeanSigSignature from SSZ: {err:?}")) - } - - pub fn verify( - &self, - public_key: &PublicKey, - epoch: u32, - message: &[u8; MESSAGE_LENGTH], - ) -> anyhow::Result { - Ok( - ::verify( - &public_key.as_lean_sig()?, - epoch, - message, - &self.as_lean_sig()?, - ), - ) - } - - /// Debug helper: decode using leansig, then re-encode and ensure bytes match. - pub fn debug_roundtrip(&self) -> anyhow::Result<()> { - let sig = self.as_lean_sig()?; - let re = sig.to_bytes(); - - anyhow::ensure!( - re.as_slice() == self.inner.as_slice(), - "Signature roundtrip mismatch: decoded->encoded bytes differ" - ); - - Ok(()) - } - - /// Debug helper: short stable fingerprint for logs. - pub fn fingerprint_hex(&self) -> String { - use alloy_primitives::hex::ToHexExt; - let bytes = self.inner.as_slice(); - let take = bytes.len().min(12); - format!("0x{}", ToHexExt::encode_hex(&bytes[..take].iter())) - } -} - -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&format!( - "0x{}", - self.as_lean_sig() - .map_err(serde::ser::Error::custom)? - .to_bytes() - .encode_hex() - )) - } -} - -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let result: String = Deserialize::deserialize(deserializer)?; - let result = alloy_primitives::hex::decode(&result).map_err(serde::de::Error::custom)?; - - Self::from_lean_sig( - LeanSigSignature::from_bytes(&result) - .map_err(|err| anyhow!("Convert to error, with error trait implemented {err:?}")) - .map_err(serde::de::Error::custom)?, - ) - .map_err(serde::de::Error::custom) - } -} diff --git a/lean_client/containers/src/slot.rs b/lean_client/containers/src/slot.rs index 17f5439..9c9c39e 100644 --- a/lean_client/containers/src/slot.rs +++ b/lean_client/containers/src/slot.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use ssz_derive::Ssz; +use ssz::Ssz; use std::cmp::Ordering; #[derive(Clone, Copy, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] diff --git a/lean_client/containers/src/state.rs b/lean_client/containers/src/state.rs index 6d56e7a..5e9ee18 100644 --- a/lean_client/containers/src/state.rs +++ b/lean_client/containers/src/state.rs @@ -1,23 +1,28 @@ -use crate::attestation::{AggregatedAttestation, AggregatedAttestations}; -use crate::validator::Validator; -use crate::{ - block::{hash_tree_root, Block, BlockBody, BlockHeader, SignedBlockWithAttestation}, - Attestation, Bytes32, Checkpoint, Config, Signature, Slot, Uint64, ValidatorIndex, -}; +use anyhow::{Context, Result, ensure}; +use serde::{Deserialize, Serialize}; +use ssz::{BitList, H256, PersistentList, Ssz, SszHash}; +use std::collections::{BTreeMap, HashMap, HashSet}; +use try_from_iterator::TryFromIterator; +use typenum::{Prod, U262144}; +use xmss::{PublicKey, Signature}; + use crate::{ - HistoricalBlockHashes, JustificationRoots, JustificationsValidators, JustifiedSlots, Validators, + AggregatedSignatureProof, Attestation, AttestationData, Checkpoint, Config, SignatureKey, Slot, + attestation::{AggregatedAttestation, AggregatedAttestations, AggregationBits}, + block::{Block, BlockBody, BlockHeader, SignedBlockWithAttestation}, + validator::{Validator, ValidatorRegistryLimit, Validators}, }; -use serde::{Deserialize, Serialize}; -use ssz::PersistentList as List; -use ssz_derive::Ssz; -use std::collections::BTreeMap; -pub const VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 -pub const JUSTIFICATION_ROOTS_LIMIT: usize = 1 << 18; // 262144 -pub const JUSTIFICATIONS_VALIDATORS_MAX: usize = - VALIDATOR_REGISTRY_LIMIT * JUSTIFICATION_ROOTS_LIMIT; +type HistoricalRootsLimit = U262144; // 2^18 + +type JustificationValidatorsLimit = Prod; -#[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] +pub type HistoricalBlockHashes = PersistentList; +pub type JustifiedSlots = BitList; +pub type JustificationValidators = BitList; +pub type JustificationRoots = PersistentList; + +#[derive(Clone, Debug, Ssz, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct State { // --- configuration (spec-local) --- @@ -46,121 +51,102 @@ pub struct State { #[serde(with = "crate::serde_helpers")] pub justifications_roots: JustificationRoots, #[serde(with = "crate::serde_helpers::bitlist")] - pub justifications_validators: JustificationsValidators, + pub justifications_validators: JustificationValidators, } impl State { - pub fn generate_genesis_with_validators( - genesis_time: Uint64, - validators: Vec, - ) -> Self { + pub fn generate_genesis_with_validators(genesis_time: u64, validators: Vec) -> Self { let body_for_root = BlockBody { attestations: Default::default(), }; let genesis_header = BlockHeader { slot: Slot(0), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32(ssz::H256::zero()), - state_root: Bytes32(ssz::H256::zero()), - body_root: hash_tree_root(&body_for_root), + proposer_index: 0, + parent_root: H256::zero(), + state_root: H256::zero(), + body_root: body_for_root.hash_tree_root(), }; - let mut validator_list = List::default(); + let mut validator_list = PersistentList::default(); for v in validators { validator_list.push(v).expect("Failed to add validator"); } Self { - config: Config { - genesis_time: genesis_time.0, - }, + config: Config { genesis_time }, slot: Slot(0), latest_block_header: genesis_header, latest_justified: Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), }, latest_finalized: Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), }, historical_block_hashes: HistoricalBlockHashes::default(), justified_slots: JustifiedSlots::default(), validators: validator_list, justifications_roots: JustificationRoots::default(), - justifications_validators: JustificationsValidators::default(), + justifications_validators: JustificationValidators::default(), } } - pub fn generate_genesis(genesis_time: Uint64, num_validators: Uint64) -> Self { + pub fn generate_genesis(genesis_time: u64, num_validators: u64) -> Self { let body_for_root = BlockBody { attestations: Default::default(), }; let header = BlockHeader { slot: Slot(0), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32(ssz::H256::zero()), - state_root: Bytes32(ssz::H256::zero()), - body_root: hash_tree_root(&body_for_root), + proposer_index: 0, + parent_root: H256::zero(), + state_root: H256::zero(), + body_root: body_for_root.hash_tree_root(), }; //TEMP: Create validators list with dummy validators - let mut validators = List::default(); - for i in 0..num_validators.0 { + let mut validators = PersistentList::default(); + for i in 0..num_validators { let validator = Validator { - pubkey: crate::public_key::PublicKey::default(), - index: Uint64(i), + pubkey: PublicKey::default(), + index: i, }; validators.push(validator).expect("Failed to add validator"); } Self { - config: Config { - genesis_time: genesis_time.0, - }, + config: Config { genesis_time }, slot: Slot(0), latest_block_header: header, latest_justified: Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), }, latest_finalized: Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), }, historical_block_hashes: HistoricalBlockHashes::default(), justified_slots: JustifiedSlots::default(), validators, justifications_roots: JustificationRoots::default(), - justifications_validators: JustificationsValidators::default(), + justifications_validators: JustificationValidators::default(), } } /// Simple RR proposer rule (round-robin). - pub fn is_proposer(&self, index: ValidatorIndex) -> bool { + pub fn is_proposer(&self, index: u64) -> bool { let num_validators = self.validators.len_u64(); if num_validators == 0 { return false; // No validators } - (self.slot.0 % num_validators) == (index.0 % num_validators) - } - - /// Get the number of validators (since PersistentList doesn't have len()) - pub fn validator_count(&self) -> usize { - let mut count: u64 = 0; - loop { - match self.validators.get(count) { - Ok(_) => count += 1, - Err(_) => break, - } - } - count as usize + (self.slot.0 % num_validators) == (index % num_validators) } - pub fn get_justifications(&self) -> BTreeMap> { + pub fn get_justifications(&self) -> BTreeMap> { // Use actual validator count, matching leanSpec - let num_validators = self.validator_count(); + let num_validators = self.validators.len_usize(); (&self.justifications_roots) .into_iter() .enumerate() @@ -181,9 +167,9 @@ impl State { .collect() } - pub fn with_justifications(mut self, map: BTreeMap>) -> Self { + pub fn with_justifications(mut self, map: BTreeMap>) -> Self { // Use actual validator count, matching leanSpec - let num_validators = self.validator_count(); + let num_validators = self.validators.len_usize(); let mut roots: Vec<_> = map.keys().cloned().collect(); roots.sort(); @@ -196,7 +182,7 @@ impl State { // Build BitList: create with length, then set bits // Each root has num_validators votes (matching leanSpec) let total_bits = roots.len() * num_validators; - let mut new_validators = JustificationsValidators::new(false, total_bits); + let mut new_validators = JustificationValidators::new(false, total_bits); for (i, r) in roots.iter().enumerate() { let v = map.get(r).expect("root present"); @@ -218,7 +204,7 @@ impl State { self } - pub fn with_historical_hashes(mut self, hashes: Vec) -> Self { + pub fn with_historical_hashes(mut self, hashes: Vec) -> Self { let mut new_hashes = HistoricalBlockHashes::default(); for h in hashes { new_hashes.push(h).expect("within limit"); @@ -227,25 +213,21 @@ impl State { self } - // updated for fork choice tests pub fn state_transition( &self, signed_block: SignedBlockWithAttestation, valid_signatures: bool, - ) -> Result { + ) -> Result { self.state_transition_with_validation(signed_block, valid_signatures, true) } - // updated for fork choice tests pub fn state_transition_with_validation( &self, signed_block: SignedBlockWithAttestation, valid_signatures: bool, validate_state_root: bool, - ) -> Result { - if !valid_signatures { - return Err("Block signatures must be valid".to_string()); - } + ) -> Result { + ensure!(valid_signatures, "invalid block signatures"); let block = &signed_block.message.block; let mut state = self.process_slots(block.slot)?; @@ -253,19 +235,16 @@ impl State { if validate_state_root { let state_for_hash = state.clone(); - let state_root = hash_tree_root(&state_for_hash); - if block.state_root != state_root { - return Err("Invalid block state root".to_string()); - } + let state_root = state_for_hash.hash_tree_root(); + + ensure!(block.state_root == state_root, "invalid block state root"); } Ok(state) } - pub fn process_slots(&self, target_slot: Slot) -> Result { - if self.slot >= target_slot { - return Err("Target slot must be in the future".to_string()); - } + pub fn process_slots(&self, target_slot: Slot) -> Result { + ensure!(self.slot < target_slot, "target slot must be in the future"); let mut state = self.clone(); @@ -280,9 +259,9 @@ impl State { pub fn process_slot(&self) -> Self { // Cache the state root in the header if not already set (matches leanSpec) // Per spec: leanSpec/src/lean_spec/subspecs/containers/state/state.py lines 173-176 - if self.latest_block_header.state_root == Bytes32(ssz::H256::zero()) { + if self.latest_block_header.state_root.is_zero() { let state_for_hash = self.clone(); - let previous_state_root = hash_tree_root(&state_for_hash); + let previous_state_root = state_for_hash.hash_tree_root(); let mut new_header = self.latest_block_header.clone(); new_header.state_root = previous_state_root; @@ -295,61 +274,50 @@ impl State { self.clone() } - pub fn process_block(&self, block: &Block) -> Result { + pub fn process_block(&self, block: &Block) -> Result { let state = self.process_block_header(block)?; - if AggregatedAttestation::has_duplicate_data(&block.body.attestations) { - return Err("Block contains duplicate AttestationData".to_string()); - } + ensure!( + !AggregatedAttestation::has_duplicate_data(&block.body.attestations), + "block contains duplicate attestation data" + ); Ok(state.process_attestations(&block.body.attestations)) } - pub fn process_block_header(&self, block: &Block) -> Result { - if !(block.slot == self.slot) { - return Err(String::from("Block slot mismatch")); - } - if !(block.slot > self.latest_block_header.slot) { - return Err(String::from("Block is older than latest header")); - } - if !self.is_proposer(block.proposer_index) { - return Err(String::from("Incorrect block proposer")); - } + pub fn process_block_header(&self, block: &Block) -> Result { + ensure!(block.slot == self.slot, "block slot mismatch"); + ensure!( + block.slot > self.latest_block_header.slot, + "block is older than latest header" + ); + ensure!( + self.is_proposer(block.proposer_index), + "incorrect block proposer" + ); // Create a mutable clone for hash computation let latest_header_for_hash = self.latest_block_header.clone(); - let parent_root = hash_tree_root(&latest_header_for_hash); - if block.parent_root != parent_root { - tracing::error!( - expected_parent_root = %format!("0x{:x}", parent_root.0), - block_parent_root = %format!("0x{:x}", block.parent_root.0), - header_slot = self.latest_block_header.slot.0, - header_proposer = self.latest_block_header.proposer_index.0, - header_parent = %format!("0x{:x}", self.latest_block_header.parent_root.0), - header_state_root = %format!("0x{:x}", self.latest_block_header.state_root.0), - header_body_root = %format!("0x{:x}", self.latest_block_header.body_root.0), - "Block parent root mismatch - debug info" - ); - return Err(String::from("Block parent root mismatch")); - } + let parent_root = latest_header_for_hash.hash_tree_root(); + + ensure!( + block.parent_root == parent_root, + "block parent root mismatch" + ); // Build new PersistentList for historical hashes let mut new_historical_hashes = HistoricalBlockHashes::default(); for hash in &self.historical_block_hashes { - new_historical_hashes.push(*hash).expect("within limit"); + new_historical_hashes.push(*hash)?; } - new_historical_hashes - .push(parent_root) - .expect("within limit"); + new_historical_hashes.push(parent_root)?; // Calculate number of empty slots (skipped slots between parent and this block) let num_empty_slots = (block.slot.0 - self.latest_block_header.slot.0 - 1) as usize; // Add ZERO_HASH entries for empty slots to historical hashes for _ in 0..num_empty_slots { - new_historical_hashes - .push(Bytes32(ssz::H256::zero())) - .expect("within limit"); + new_historical_hashes.push(H256::zero())?; } // Extend justified_slots to cover slots from finalized_slot+1 to last_materialized_slot @@ -386,14 +354,14 @@ impl State { }; let body_for_hash = block.body.clone(); - let body_root = hash_tree_root(&body_for_hash); + let body_root = body_for_hash.hash_tree_root(); let new_latest_block_header = BlockHeader { slot: block.slot, proposer_index: block.proposer_index, parent_root: block.parent_root, body_root, - state_root: Bytes32(ssz::H256::zero()), + state_root: H256::zero(), }; let mut new_latest_justified = self.latest_justified.clone(); @@ -465,9 +433,9 @@ impl State { /// Slots at or before finalized_slot are implicitly justified (not stored in the bitlist). fn process_single_attestation( &self, - vote: &crate::attestation::AttestationData, + vote: &AttestationData, validator_ids: &[u64], - justifications: &mut BTreeMap>, + justifications: &mut BTreeMap>, latest_justified: &mut Checkpoint, latest_finalized: &mut Checkpoint, justified_slots_working: &mut Vec, @@ -514,7 +482,7 @@ impl State { .unwrap_or(false); // Ignore votes that reference zero-hash slots (per leanSpec) - if source_root.0.is_zero() || target_root.0.is_zero() { + if source_root.is_zero() || target_root.is_zero() { return; } @@ -529,8 +497,8 @@ impl State { tracing::debug!( source_slot = source_slot.0, target_slot = target_slot.0, - source_root = %format!("0x{:x}", source_root.0), - target_root = %format!("0x{:x}", target_root.0), + source_root = %format!("0x{:x}", source_root), + target_root = %format!("0x{:x}", target_root), validator_count = validator_ids.len(), source_is_justified, target_already_justified, @@ -554,7 +522,7 @@ impl State { } if !justifications.contains_key(&target_root) { - justifications.insert(target_root, vec![false; self.validator_count()]); + justifications.insert(target_root, vec![false; self.validators.len_usize()]); } for &validator_id in validator_ids { @@ -569,11 +537,11 @@ impl State { if let Some(votes) = justifications.get(&target_root) { let num_validators = self.validators.len_u64() as usize; let count = votes.iter().filter(|&&v| v).count(); - let threshold = (2 * num_validators).div_ceil(3); + let threshold = (2usize * num_validators).div_ceil(3); tracing::info!( target_slot = target_slot.0, - target_root = %format!("0x{:x}", target_root.0), + target_root = %format!("0x{:x}", target_root), vote_count = count, num_validators, threshold, @@ -585,7 +553,7 @@ impl State { if 3 * count >= 2 * num_validators { tracing::info!( target_slot = target_slot.0, - target_root = %format!("0x{:x}", target_root.0), + target_root = %format!("0x{:x}", target_root), "Justification threshold reached" ); *latest_justified = vote.target.clone(); @@ -616,7 +584,7 @@ impl State { fn finalize_attestation_processing( &self, - justifications: BTreeMap>, + justifications: BTreeMap>, latest_justified: Checkpoint, latest_finalized: Checkpoint, justified_slots_working: Vec, @@ -659,148 +627,84 @@ impl State { pub fn build_block( &self, slot: Slot, - proposer_index: ValidatorIndex, - parent_root: Bytes32, + proposer_index: u64, + parent_root: H256, initial_attestations: Option>, available_attestations: Option>, - known_block_roots: Option<&std::collections::HashSet>, - gossip_signatures: Option<&std::collections::HashMap>, - aggregated_payloads: Option< - &std::collections::HashMap>, - >, - ) -> Result< - ( - Block, - Self, - Vec, - Vec, - ), - String, - > { - use crate::attestation::{AggregatedAttestation, SignatureKey}; - + known_block_roots: Option<&HashSet>, + gossip_signatures: Option<&HashMap>, + aggregated_payloads: Option<&HashMap>>, + ) -> Result<( + Block, + Self, + Vec, + Vec, + )> { // Initialize attestation set let mut attestations = initial_attestations.unwrap_or_default(); - // Advance state to target slot - let pre_state = self.process_slots(slot)?; - // Fixed-point attestation collection loop // Iteratively add valid attestations until no new ones can be added loop { // Create candidate block with current attestation set let aggregated = AggregatedAttestation::aggregate_by_data(&attestations); - let mut attestations_list = AggregatedAttestations::default(); - for att in &aggregated { - attestations_list - .push(att.clone()) - .map_err(|e| format!("Failed to push attestation: {:?}", e))?; - } let candidate_block = Block { slot, proposer_index, parent_root, - state_root: Bytes32(ssz::H256::zero()), + state_root: H256::zero(), body: BlockBody { - attestations: attestations_list, + attestations: AggregatedAttestations::try_from_iter(aggregated.into_iter())?, }, }; // Apply state transition to get the post-block state - let post_state = pre_state.process_block(&candidate_block)?; - - // If no available attestations pool, skip fixed-point iteration - let available = match &available_attestations { - Some(avail) => avail, - None => { - // No fixed-point: compute signatures and return - let (aggregated_attestations, aggregated_proofs) = self - .compute_aggregated_signatures( - &attestations, - gossip_signatures, - aggregated_payloads, - )?; - - let mut final_attestations_list = AggregatedAttestations::default(); - for att in &aggregated_attestations { - final_attestations_list - .push(att.clone()) - .map_err(|e| format!("Failed to push attestation: {:?}", e))?; - } + let post_state = self.process_slots(slot)?.process_block(&candidate_block)?; - // IMPORTANT: Recompute post_state using the FINAL attestations. - // The original post_state was computed from candidate_block with ALL attestations, - // but final_attestations_list may have fewer attestations (only those with signatures). - // We must use the same attestations for state computation and the block body. - let final_candidate_block = Block { - slot, - proposer_index, - parent_root, - state_root: Bytes32(ssz::H256::zero()), - body: BlockBody { - attestations: final_attestations_list.clone(), - }, - }; - let final_post_state = pre_state.process_block(&final_candidate_block)?; - - let final_block = Block { - slot, - proposer_index, - parent_root, - state_root: hash_tree_root(&final_post_state), - body: BlockBody { - attestations: final_attestations_list, - }, - }; - - return Ok(( - final_block, - final_post_state, - aggregated_attestations, - aggregated_proofs, - )); - } + let Some(ref available_attestations) = available_attestations else { + // No attestation source provided: done after computing post_state + break; + }; + + let Some(known_block_roots) = known_block_roots else { + // No attestation source provided: done after computing post_state + break; }; - // Find new valid attestations from available pool - let mut new_attestations: Vec = Vec::new(); - let current_data_roots: std::collections::HashSet<_> = attestations - .iter() - .map(|a| a.data.data_root_bytes()) - .collect(); + // Find new valid attestations matching post-state justification + let mut new_attestations = Vec::new(); - for attestation in available { - // Skip if already included - if current_data_roots.contains(&attestation.data.data_root_bytes()) { - continue; - } + for attestation in available_attestations { + let data = &attestation.data; + let validator_id = attestation.validator_id; + let data_root = data.hash_tree_root(); + let sig_key = SignatureKey::new(validator_id, data_root); - // Validate attestation against post-state - // Source must match post-state's justified checkpoint - if attestation.data.source != post_state.latest_justified { + // Skip if target block is unknown + if !known_block_roots.contains(&data.head.root) { continue; } - // Target must be after source - if attestation.data.target.slot <= attestation.data.source.slot { + // Skip if attestation source does not match post-state's latest justified + if data.source != post_state.latest_justified { continue; } - // Target block must be known (if known_block_roots provided) - if let Some(known_roots) = known_block_roots { - if !known_roots.contains(&attestation.data.target.root) { - continue; - } + // Avoid adding duplicates of attestations already in the candidate set + if attestations.contains(attestation) { + continue; } - // Check if we have a signature for this attestation - let data_root = attestation.data.data_root_bytes(); - let sig_key = SignatureKey::new(attestation.validator_id.0, data_root); + // We can only include an attestation if we have some way to later provide + // an aggregated proof for its group: + // - either a per validator XMSS signature from gossip, or + // - at least one aggregated proof learned from a block that references + // this validator+data. let has_gossip_sig = - gossip_signatures.map_or(false, |gs| gs.contains_key(&sig_key)); + gossip_signatures.is_some_and(|sigs| sigs.contains_key(&sig_key)); let has_block_proof = - aggregated_payloads.map_or(false, |ap| ap.contains_key(&sig_key)); + aggregated_payloads.is_some_and(|payloads| payloads.contains_key(&sig_key)); if has_gossip_sig || has_block_proof { new_attestations.push(attestation.clone()); @@ -809,98 +713,79 @@ impl State { // Fixed point reached: no new attestations found if new_attestations.is_empty() { - // Compute aggregated signatures - let (aggregated_attestations, aggregated_proofs) = self - .compute_aggregated_signatures( - &attestations, - gossip_signatures, - aggregated_payloads, - )?; - - let mut final_attestations_list = AggregatedAttestations::default(); - for att in &aggregated_attestations { - final_attestations_list - .push(att.clone()) - .map_err(|e| format!("Failed to push attestation: {:?}", e))?; - } - - // IMPORTANT: Recompute post_state using the FINAL attestations. - // The original post_state was computed from candidate_block with ALL attestations, - // but final_attestations_list may have fewer attestations (only those with signatures). - // We must use the same attestations for state computation and the block body. - let final_candidate_block = Block { - slot, - proposer_index, - parent_root, - state_root: Bytes32(ssz::H256::zero()), - body: BlockBody { - attestations: final_attestations_list.clone(), - }, - }; - let final_post_state = pre_state.process_block(&final_candidate_block)?; - - let final_block = Block { - slot, - proposer_index, - parent_root, - state_root: hash_tree_root(&final_post_state), - body: BlockBody { - attestations: final_attestations_list, - }, - }; - - return Ok(( - final_block, - final_post_state, - aggregated_attestations, - aggregated_proofs, - )); + break; } // Add new attestations and continue iteration attestations.extend(new_attestations); } + + let (aggregated_attestations, aggregated_signatures) = self.compute_aggregated_signatures( + &attestations, + gossip_signatures, + aggregated_payloads, + )?; + + let mut final_block = Block { + slot, + proposer_index, + parent_root, + state_root: H256::zero(), + body: BlockBody { + attestations: AggregatedAttestations::try_from_iter( + aggregated_attestations.clone(), + )?, + }, + }; + + let post_state = self.process_slots(slot)?.process_block(&final_block)?; + + final_block.state_root = post_state.hash_tree_root(); + + Ok(( + final_block, + post_state, + aggregated_attestations, + aggregated_signatures, + )) } pub fn compute_aggregated_signatures( &self, attestations: &[Attestation], - gossip_signatures: Option<&std::collections::HashMap>, - aggregated_payloads: Option< - &std::collections::HashMap>, - >, - ) -> Result< - ( - Vec, - Vec, - ), - String, - > { - use crate::attestation::{AggregatedAttestation, AggregationBits, SignatureKey}; - use std::collections::HashSet; - - let mut results: Vec<(AggregatedAttestation, crate::AggregatedSignatureProof)> = Vec::new(); + gossip_signatures: Option<&HashMap>, + aggregated_payloads: Option<&HashMap>>, + ) -> Result<(Vec, Vec)> { + let mut results: Vec<(AggregatedAttestation, AggregatedSignatureProof)> = Vec::new(); // Group individual attestations by data for aggregated in AggregatedAttestation::aggregate_by_data(attestations) { let data = &aggregated.data; - let data_root = data.data_root_bytes(); + let data_root = data.hash_tree_root(); let validator_ids = aggregated.aggregation_bits.to_validator_indices(); // Phase 1: Gossip Collection // Try to collect individual signatures from gossip network - let mut gossip_ids: Vec = Vec::new(); - let mut _gossip_sigs_collected: Vec = Vec::new(); - let mut remaining: HashSet = HashSet::new(); - - if let Some(gossip_sigs) = gossip_signatures { - for vid in &validator_ids { - let key = SignatureKey::new(*vid, data_root); - if let Some(sig) = gossip_sigs.get(&key) { - gossip_ids.push(*vid); - _gossip_sigs_collected.push(sig.clone()); + let mut gossip_sigs = Vec::new(); + let mut gossip_keys = Vec::new(); + let mut gossip_ids = Vec::new(); + + let mut remaining = HashSet::new(); + + if let Some(gossip_signatures) = gossip_signatures { + for vid in validator_ids { + let key = SignatureKey::new(vid, data_root); + if let Some(sig) = gossip_signatures.get(&key) { + gossip_sigs.push(sig.clone()); + gossip_keys.push( + self.validators + .get(vid) + .map(|v| v.pubkey.clone()) + .context(format!("invalid validator id {vid}"))?, + ); + gossip_ids.push(vid); } else { - remaining.insert(*vid); + remaining.insert(vid); } } } else { @@ -917,11 +802,13 @@ impl State { if !gossip_ids.is_empty() { let participants = AggregationBits::from_validator_indices(&gossip_ids); - // Create proof placeholder (matches Python test_mode behavior) - // TODO: Call actual aggregation when lean-multisig supports proper encoding - let proof_data = crate::MultisigAggregatedSignature::new(Vec::new()) - .expect("Empty proof should always be valid"); - let proof = crate::AggregatedSignatureProof::new(participants.clone(), proof_data); + let proof = AggregatedSignatureProof::aggregate( + participants.clone(), + gossip_keys, + gossip_sigs, + data_root, + data.slot.0 as u32, + )?; results.push(( AggregatedAttestation { @@ -934,21 +821,28 @@ impl State { // Phase 2: Fallback to block proofs using greedy set-cover // Goal: Cover remaining validators with minimum number of proofs - while !remaining.is_empty() { - let payloads = match aggregated_payloads { - Some(p) => p, - None => break, + loop { + let Some(payloads) = aggregated_payloads else { + break; }; // Pick any remaining validator to find candidate proofs - let target_id = *remaining.iter().next().unwrap(); + let Some(target_id) = remaining.iter().next().copied() else { + break; + }; + let key = SignatureKey::new(target_id, data_root); - let candidates = match payloads.get(&key) { - Some(proofs) if !proofs.is_empty() => proofs, - _ => break, // No proofs found for this validator + let Some(candidates) = payloads.get(&key) else { + // No proofs found for this validator + break; }; + if candidates.is_empty() { + // Same as before, no proofs found for this validator + break; + } + // Greedy selection: find proof covering most remaining validators // For each candidate proof, compute intersection with remaining validators let (best_proof, covered_set) = candidates @@ -961,7 +855,7 @@ impl State { (proof, intersection) }) .max_by_key(|(_, intersection)| intersection.len()) - .expect("candidates is non-empty"); + .context("greedy algoritm failure: candidates were empty")?; // Guard: If best proof has zero overlap, stop if covered_set.is_empty() { diff --git a/lean_client/containers/src/status.rs b/lean_client/containers/src/status.rs index d68c7c3..e2b9b22 100644 --- a/lean_client/containers/src/status.rs +++ b/lean_client/containers/src/status.rs @@ -1,6 +1,6 @@ use crate::Checkpoint; use serde::{Deserialize, Serialize}; -use ssz_derive::Ssz; +use ssz::Ssz; #[derive(Clone, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] pub struct Status { diff --git a/lean_client/containers/src/test_vectors/mod.rs b/lean_client/containers/src/test_vectors/mod.rs deleted file mode 100644 index a78b58c..0000000 --- a/lean_client/containers/src/test_vectors/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -pub mod block_processing; -pub mod runner; -pub mod state_transition; -pub mod vote_processing; - -use crate::*; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct TestCase { - pub description: String, - pub pre: T, - pub post: Option, - pub blocks: Option>, - pub votes: Option>, - pub valid: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TestVector { - pub test_cases: Vec>, - pub config: Config, -} diff --git a/lean_client/containers/src/types.rs b/lean_client/containers/src/types.rs deleted file mode 100644 index 7d9aa4d..0000000 --- a/lean_client/containers/src/types.rs +++ /dev/null @@ -1,60 +0,0 @@ -use hex::FromHex; -use serde::{Deserialize, Serialize}; -use ssz::H256; -use ssz_derive::Ssz; -use std::fmt; -use std::hash::Hash; -use std::str::FromStr; - -#[derive( - Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Ssz, Default, Serialize, Deserialize, -)] -#[ssz(transparent)] -pub struct Bytes32(pub H256); - -#[derive( - Clone, Hash, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Ssz, Default, Serialize, Deserialize, -)] -#[ssz(transparent)] -pub struct Uint64(pub u64); - -#[derive(Clone, Hash, Copy, Debug, PartialEq, Eq, Ssz, Default, Serialize, Deserialize)] -#[ssz(transparent)] -pub struct ValidatorIndex(pub u64); - -impl FromStr for Bytes32 { - type Err = hex::FromHexError; - - fn from_str(s: &str) -> Result { - let bytes: [u8; 32] = <[u8; 32]>::from_hex(s)?; - Ok(Bytes32(H256::from(bytes))) - } -} - -impl fmt::Display for Bytes32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", hex::encode(self.0.as_bytes())) - } -} - -// Type-level constants for SSZ collection limits -use crate::validator::Validator; -use typenum::{Prod, U1000, U1073741824, U262144, U4, U4096}; -// 2^18, 4096 * 262144 - -/// Type-level number for 4000 bytes (signature size) = 4 * 1000 -pub type U4000 = Prod; - -/// List of historical block root hashes (SSZList) -pub type HistoricalBlockHashes = ssz::PersistentList; - -pub type Validators = ssz::PersistentList; - -/// List of justified block roots (SSZList) -pub type JustificationRoots = ssz::PersistentList; - -/// Bitlist tracking justified slots (BitList) -pub type JustifiedSlots = ssz::BitList; // 2^18 - -/// Bitlist for tracking validator justifications (BitList) -pub type JustificationsValidators = ssz::BitList; // 4096 * 262144 diff --git a/lean_client/containers/src/validator.rs b/lean_client/containers/src/validator.rs index a6580da..c4ed985 100644 --- a/lean_client/containers/src/validator.rs +++ b/lean_client/containers/src/validator.rs @@ -1,10 +1,16 @@ use serde::{Deserialize, Serialize}; -use ssz_derive::Ssz; +use ssz::{PersistentList, Ssz}; +use typenum::U4096; +use xmss::PublicKey; -#[derive(Clone, Debug, PartialEq, Eq, Default, Ssz, Serialize, Deserialize)] +// todo(containers): default implementation doesn't make sense here +#[derive(Clone, Debug, Ssz, Serialize, Deserialize, Default)] pub struct Validator { - // This now uses new XMSS PublicKey struct - pub pubkey: crate::public_key::PublicKey, + pub pubkey: PublicKey, #[serde(default)] - pub index: crate::Uint64, + pub index: u64, } + +pub type ValidatorRegistryLimit = U4096; + +pub type Validators = PersistentList; diff --git a/lean_client/containers/tests/test_vectors/mod.rs b/lean_client/containers/tests/test_vectors/mod.rs index 5d26d0c..b753ea4 100644 --- a/lean_client/containers/tests/test_vectors/mod.rs +++ b/lean_client/containers/tests/test_vectors/mod.rs @@ -8,7 +8,7 @@ pub mod genesis; pub mod runner; pub mod verify_signatures; -use containers::{block::Block, block::SignedBlockWithAttestation, state::State, Slot}; +use containers::{Block, SignedBlockWithAttestation, Slot, State}; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; use std::collections::HashMap; diff --git a/lean_client/containers/tests/test_vectors/runner.rs b/lean_client/containers/tests/test_vectors/runner.rs index 5aa84d7..0bd7c85 100644 --- a/lean_client/containers/tests/test_vectors/runner.rs +++ b/lean_client/containers/tests/test_vectors/runner.rs @@ -1,5 +1,6 @@ +use ssz::SszHash; + use super::*; -use containers::block::hash_tree_root; use std::fs; use std::path::Path; @@ -34,7 +35,7 @@ impl TestRunner { let state_after_slots = state.process_slots(block.slot)?; // Compute the parent root from our current latest_block_header - let computed_parent_root = hash_tree_root(&state_after_slots.latest_block_header); + let computed_parent_root = state_after_slots.latest_block_header.hash_tree_root(); // Verify the block's parent_root matches what we computed if block.parent_root != computed_parent_root { @@ -56,7 +57,7 @@ impl TestRunner { state = new_state; // Compute the state root after processing - let computed_state_root = hash_tree_root(&state); + let computed_state_root = state.hash_tree_root(); // Verify the computed state_root matches the expected one from the vector if block.state_root != computed_state_root { @@ -147,7 +148,7 @@ impl TestRunner { let state_after_slots = state.process_slots(block.slot)?; // Compute the parent root from our current latest_block_header - let computed_parent_root = hash_tree_root(&state_after_slots.latest_block_header); + let computed_parent_root = state_after_slots.latest_block_header.hash_tree_root(); // Verify the block's parent_root matches what we computed if block.parent_root != computed_parent_root { @@ -169,7 +170,7 @@ impl TestRunner { state = new_state; // Compute the state root after processing - let computed_state_root = hash_tree_root(&state); + let computed_state_root = state.hash_tree_root(); // Verify the computed state_root matches the expected one from the vector if block.state_root != computed_state_root { @@ -268,7 +269,7 @@ impl TestRunner { let state_after_slots = state.process_slots(block.slot)?; // Compute the parent root from our current latest_block_header - let computed_parent_root = hash_tree_root(&state_after_slots.latest_block_header); + let computed_parent_root = state_after_slots.latest_block_header.hash_tree_root(); // Verify the block's parent_root matches what we computed if block.parent_root != computed_parent_root { @@ -289,7 +290,7 @@ impl TestRunner { state = new_state; // Compute the state root after processing - let computed_state_root = hash_tree_root(&state); + let computed_state_root = state.hash_tree_root(); // Verify the computed state_root matches the expected one from the vector if block.state_root != computed_state_root { @@ -388,7 +389,7 @@ impl TestRunner { let state_after_slots = state.process_slots(block.slot)?; // Compute the parent root from our current latest_block_header - let computed_parent_root = hash_tree_root(&state_after_slots.latest_block_header); + let computed_parent_root = state_after_slots.latest_block_header.hash_tree_root(); // Verify the block's parent_root matches what we computed if block.parent_root != computed_parent_root { @@ -418,7 +419,7 @@ impl TestRunner { state = new_state; // Compute the state root after processing - let computed_state_root = hash_tree_root(&state); + let computed_state_root = state.hash_tree_root(); // Verify the computed state_root matches the expected one from the block if block.state_root != computed_state_root { @@ -539,14 +540,16 @@ impl TestRunner { match result { Ok(new_state) => { // Block processing succeeded, now validate state root - let computed_state_root = hash_tree_root(&new_state); + let computed_state_root = new_state.hash_tree_root(); if block.state_root != computed_state_root { error_occurred = true; println!(" ✓ Correctly rejected: Invalid block state root"); break; // Stop at first error } else { - println!(" \x1b[31m✗ FAIL: Block processed successfully - but should have failed!\x1b[0m\n"); + println!( + " \x1b[31m✗ FAIL: Block processed successfully - but should have failed!\x1b[0m\n" + ); return Err( "Expected block processing to fail, but it succeeded".into() ); @@ -621,44 +624,20 @@ impl TestRunner { // // NOTE: Disabled until test vector files are regenerated for devnet2 BlockSignatures format. // The current JSON test vectors use signature.data array instead of attestation_signatures + proposer_signature. - pub fn run_verify_signatures_test>( - path: P, - ) -> Result<(), Box> { - let json_content = fs::read_to_string(path.as_ref())?; - - // Phase 1: parse minimally to detect `expectException` even if typed parsing fails. - let raw: serde_json::Value = serde_json::from_str(&json_content)?; - - let expect_exception = raw - .get("tests") - .and_then(|t| t.as_object()) - .and_then(|obj| obj.values().next()) - .and_then(|tc| tc.get("expectException")) - .and_then(|v| v.as_str()) - .map(|s| s.to_owned()); + pub fn run_verify_signatures_test>(path: P) { + let json_content = fs::read_to_string(path.as_ref()).unwrap(); // Phase 2: parse into the typed structure. - let test_file: VerifySignaturesTestVectorFile = match serde_json::from_str(&json_content) { - Ok(v) => v, - Err(e) => { - if let Some(ref ex) = expect_exception { - println!( - "\nExpected exception: {} (typed JSON parse failed: {})", - ex, e - ); - println!("\n\x1b[32m✓ PASS\x1b[0m\n"); - return Ok(()); - } - return Err(Box::new(e)); - } - }; + let test_file: VerifySignaturesTestVectorFile = + serde_json::from_str(&json_content).unwrap(); // Get the first (and only) test case from the file let (test_name, test_case) = test_file .tests .into_iter() .next() - .ok_or("No test case found in JSON")?; + .ok_or("No test case found in JSON") + .unwrap(); println!("\n{}: {}", test_name, test_case.info.description); @@ -669,14 +648,14 @@ impl TestRunner { println!(" Block slot: {}", signed_block.message.block.slot.0); println!( " Proposer index: {}", - signed_block.message.block.proposer_index.0 + signed_block.message.block.proposer_index ); let attestation_count = signed_block.message.block.body.attestations.len_u64(); println!(" Attestations in block: {}", attestation_count); println!( " Proposer attestation validator: {}", - signed_block.message.proposer_attestation.validator_id.0 + signed_block.message.proposer_attestation.validator_id ); // Check if we expect this test to fail @@ -686,13 +665,15 @@ impl TestRunner { // Verify signatures - we expect this to fail (return Err) match signed_block.verify_signatures(anchor_state) { Ok(()) => { - println!(" \x1b[31m✗ FAIL: Signatures verified successfully but should have failed!\x1b[0m\n"); - return Err("Expected signature verification to fail, but it succeeded".into()); + println!( + " \x1b[31m✗ FAIL: Signatures verified successfully but should have failed!\x1b[0m\n" + ); + panic!("Expected signature verification to fail, but it succeeded"); } Err(_) => { println!(" ✓ Correctly rejected: Invalid signatures detected"); println!("\n\x1b[32m✓ PASS\x1b[0m\n"); - return Ok(()); + return; } } } @@ -701,14 +682,13 @@ impl TestRunner { Ok(()) => { println!(" ✓ All signatures verified successfully"); println!("\n\x1b[32m✓ PASS\x1b[0m\n"); - Ok(()) } Err(e) => { println!( " \x1b[31m✗ FAIL: Signature verification failed: {}\x1b[0m\n", e ); - Err(format!("Signature verification failed: {}", e).into()) + panic!("Signature verification failed: {}", e) } } } diff --git a/lean_client/containers/tests/test_vectors/verify_signatures.rs b/lean_client/containers/tests/test_vectors/verify_signatures.rs index 7e52cff..011ca2e 100644 --- a/lean_client/containers/tests/test_vectors/verify_signatures.rs +++ b/lean_client/containers/tests/test_vectors/verify_signatures.rs @@ -17,5 +17,5 @@ fn verify_signatures(spec_file: &str) { .join("..") .join(spec_file); - TestRunner::run_verify_signatures_test(test_path).unwrap(); + TestRunner::run_verify_signatures_test(test_path); } diff --git a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs index 5d1e4dc..4a49935 100644 --- a/lean_client/containers/tests/unit_tests/attestation_aggregation.rs +++ b/lean_client/containers/tests/unit_tests/attestation_aggregation.rs @@ -1,26 +1,24 @@ #[cfg(test)] mod tests { - use containers::attestation::{ - AggregatedAttestation, AggregationBits, Attestation, AttestationData, + use containers::{ + AggregatedAttestation, AggregationBits, Attestation, AttestationData, Checkpoint, Slot, }; - use containers::checkpoint::Checkpoint; - use containers::slot::Slot; - use containers::{Bytes32, Uint64}; + use ssz::H256; #[test] fn test_aggregated_attestation_structure() { let att_data = AttestationData { slot: Slot(5), head: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(4), }, target: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(3), }, source: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(2), }, }; @@ -46,45 +44,45 @@ mod tests { let att_data1 = AttestationData { slot: Slot(5), head: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(4), }, target: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(3), }, source: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(2), }, }; let att_data2 = AttestationData { slot: Slot(6), head: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(5), }, target: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(4), }, source: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(3), }, }; let attestations = vec![ Attestation { - validator_id: Uint64(1), + validator_id: 1, data: att_data1.clone(), }, Attestation { - validator_id: Uint64(3), + validator_id: 3, data: att_data1.clone(), }, Attestation { - validator_id: Uint64(5), + validator_id: 5, data: att_data2.clone(), }, ]; @@ -117,21 +115,21 @@ mod tests { let att_data = AttestationData { slot: Slot(5), head: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(4), }, target: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(3), }, source: Checkpoint { - root: Bytes32::default(), + root: H256::default(), slot: Slot(2), }, }; let attestations = vec![Attestation { - validator_id: Uint64(5), + validator_id: 5, data: att_data.clone(), }]; let aggregated = AggregatedAttestation::aggregate_by_data(&attestations); diff --git a/lean_client/containers/tests/unit_tests/common.rs b/lean_client/containers/tests/unit_tests/common.rs index 2981a2c..43e9938 100644 --- a/lean_client/containers/tests/unit_tests/common.rs +++ b/lean_client/containers/tests/unit_tests/common.rs @@ -3,18 +3,17 @@ //! Helper functions for creating test blocks, states, and attestations //! using the devnet2 data structures. -use containers::block::BlockSignatures; use containers::{ - block::{hash_tree_root, Block, BlockBody, BlockHeader}, - checkpoint::Checkpoint, - slot::Slot, - state::State, - types::{Bytes32, ValidatorIndex}, - AggregatedAttestation, Attestation, Attestations, BlockWithAttestation, Config, Signature, - SignedBlockWithAttestation, Validators, + AggregatedAttestation, Attestation, Attestations, Block, BlockBody, BlockHeader, + BlockSignatures, BlockWithAttestation, Checkpoint, Config, SignedBlockWithAttestation, Slot, + State, Validators, }; -use ssz::PersistentList; +use containers::{ + HistoricalBlockHashes, JustificationRoots, JustificationValidators, JustifiedSlots, Validator, +}; +use ssz::{H256, PersistentList, SszHash}; use typenum::U4096; +use xmss::Signature; pub const DEVNET_CONFIG_VALIDATOR_REGISTRY_LIMIT: usize = 1 << 12; // 4096 pub const TEST_VALIDATOR_COUNT: usize = 4; // Actual validator count used in tests @@ -56,9 +55,9 @@ pub fn create_block( let block_message = Block { slot: Slot(slot), - proposer_index: ValidatorIndex(slot % 10), - parent_root: hash_tree_root(parent_header), - state_root: Bytes32(ssz::H256::zero()), + proposer_index: slot % 10, + parent_root: parent_header.hash_tree_root(), + state_root: H256::zero(), body: body, }; @@ -89,16 +88,16 @@ pub fn create_attestations(indices: &[usize]) -> Vec { pub fn sample_block_header() -> BlockHeader { BlockHeader { slot: Slot(0), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32(ssz::H256::zero()), - state_root: Bytes32(ssz::H256::zero()), - body_root: Bytes32(ssz::H256::zero()), + proposer_index: 0, + parent_root: H256::zero(), + state_root: H256::zero(), + body_root: H256::zero(), } } pub fn sample_checkpoint() -> Checkpoint { Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), } } @@ -108,17 +107,12 @@ pub fn base_state(config: Config) -> State { } pub fn base_state_with_validators(config: Config, num_validators: usize) -> State { - use containers::{ - validator::Validator, HistoricalBlockHashes, JustificationRoots, JustificationsValidators, - JustifiedSlots, Uint64, - }; - // Create validators list with the specified number of validators let mut validators = Validators::default(); for i in 0..num_validators { let validator = Validator { pubkey: Default::default(), - index: Uint64(i as u64), + index: i as u64, }; validators.push(validator).expect("within limit"); } @@ -133,7 +127,7 @@ pub fn base_state_with_validators(config: Config, num_validators: usize) -> Stat justified_slots: JustifiedSlots::default(), validators, justifications_roots: JustificationRoots::default(), - justifications_validators: JustificationsValidators::default(), + justifications_validators: JustificationValidators::default(), } } diff --git a/lean_client/containers/tests/unit_tests/state_basic.rs b/lean_client/containers/tests/unit_tests/state_basic.rs index 5fa59f3..d3bbb19 100644 --- a/lean_client/containers/tests/unit_tests/state_basic.rs +++ b/lean_client/containers/tests/unit_tests/state_basic.rs @@ -2,23 +2,19 @@ //! //! Tests for genesis generation, proposer selection, slot rules, and hash tree root. +use containers::{Block, BlockBody, Slot, State}; // tests/state_basic.rs -use containers::{ - block::{hash_tree_root, BlockBody}, - state::State, - types::Uint64, - ValidatorIndex, -}; use pretty_assertions::assert_eq; #[path = "common.rs"] mod common; use common::sample_config; +use ssz::{H256, SszHash}; #[test] fn test_generate_genesis() { let config = sample_config(); - let state = State::generate_genesis(Uint64(config.genesis_time), Uint64(4)); + let state = State::generate_genesis(config.genesis_time, 4); assert_eq!(state.config, config); assert_eq!(state.slot.0, 0); @@ -28,7 +24,7 @@ fn test_generate_genesis() { }; assert_eq!( state.latest_block_header.body_root, - hash_tree_root(&empty_body) + empty_body.hash_tree_root() ); // Check that collections are empty by trying to get the first element @@ -40,14 +36,12 @@ fn test_generate_genesis() { #[test] fn test_proposer_round_robin() { - let state = State::generate_genesis(Uint64(0), Uint64(4)); - assert!(state.is_proposer(containers::types::ValidatorIndex(0))); + let state = State::generate_genesis(0, 4); + assert!(state.is_proposer(0)); } #[test] fn test_slot_justifiability_rules() { - use containers::slot::Slot; - assert!(Slot(1).is_justifiable_after(Slot(0))); assert!(Slot(9).is_justifiable_after(Slot(0))); // perfect square assert!(Slot(6).is_justifiable_after(Slot(0))); // pronic (2*3) @@ -58,14 +52,14 @@ fn test_hash_tree_root() { let body = BlockBody { attestations: ssz::PersistentList::default(), }; - let block = containers::block::Block { - slot: containers::slot::Slot(1), - proposer_index: ValidatorIndex(0), - parent_root: containers::types::Bytes32(ssz::H256::zero()), - state_root: containers::types::Bytes32(ssz::H256::zero()), + let block = Block { + slot: Slot(1), + proposer_index: 0, + parent_root: H256::zero(), + state_root: H256::zero(), body, }; - let root = hash_tree_root(&block); - assert_ne!(root, containers::types::Bytes32(ssz::H256::zero())); + let root = block.hash_tree_root(); + assert_ne!(root, H256::zero()); } diff --git a/lean_client/containers/tests/unit_tests/state_justifications.rs b/lean_client/containers/tests/unit_tests/state_justifications.rs index 61bf09c..661b8e0 100644 --- a/lean_client/containers/tests/unit_tests/state_justifications.rs +++ b/lean_client/containers/tests/unit_tests/state_justifications.rs @@ -3,14 +3,14 @@ //! Tests for justification get/set operations and roundtrip verification. // tests/state_justifications.rs -use containers::{state::State, types::Bytes32, Config}; +use containers::{Config, State}; use pretty_assertions::assert_eq; use rstest::{fixture, rstest}; -use ssz::PersistentList as List; +use ssz::{H256, PersistentList as List}; #[path = "common.rs"] mod common; -use common::{base_state, create_attestations, sample_config, TEST_VALIDATOR_COUNT}; +use common::{TEST_VALIDATOR_COUNT, base_state, create_attestations, sample_config}; #[fixture] fn config() -> Config { @@ -36,7 +36,7 @@ fn test_get_justifications_empty() { #[test] fn test_get_justifications_single_root() { let mut state = state(sample_config()); - let root1 = Bytes32(ssz::H256::from_slice(&[1u8; 32])); + let root1 = H256::from_slice(&[1u8; 32]); let mut votes1 = vec![false; TEST_VALIDATOR_COUNT]; votes1[2] = true; @@ -64,9 +64,9 @@ fn test_get_justifications_single_root() { #[test] fn test_get_justifications_multiple_roots() { let mut state = state(sample_config()); - let root1 = Bytes32(ssz::H256::from_slice(&[1u8; 32])); - let root2 = Bytes32(ssz::H256::from_slice(&[2u8; 32])); - let root3 = Bytes32(ssz::H256::from_slice(&[3u8; 32])); + let root1 = H256::from_slice(&[1u8; 32]); + let root2 = H256::from_slice(&[2u8; 32]); + let root3 = H256::from_slice(&[3u8; 32]); let limit = TEST_VALIDATOR_COUNT; @@ -111,9 +111,7 @@ fn test_with_justifications_empty() { let mut initial_state = base_state(config.clone()); let mut roots_list = List::default(); - roots_list - .push(Bytes32(ssz::H256::from_slice(&[1u8; 32]))) - .unwrap(); + roots_list.push(H256::from_slice(&[1u8; 32])).unwrap(); initial_state.justifications_roots = roots_list; let mut bitlist = ssz::BitList::with_length(TEST_VALIDATOR_COUNT); @@ -135,8 +133,8 @@ fn test_with_justifications_empty() { #[test] fn test_with_justifications_deterministic_order() { let state = state(sample_config()); - let root1 = Bytes32(ssz::H256::from_slice(&[1u8; 32])); - let root2 = Bytes32(ssz::H256::from_slice(&[2u8; 32])); + let root1 = H256::from_slice(&[1u8; 32]); + let root2 = H256::from_slice(&[2u8; 32]); let limit = TEST_VALIDATOR_COUNT; let votes1 = vec![false; limit]; @@ -168,7 +166,7 @@ fn test_with_justifications_deterministic_order() { #[should_panic(expected = "vote vector must match validator count")] fn test_with_justifications_invalid_length() { let state = state(sample_config()); - let root1 = Bytes32(ssz::H256::from_slice(&[1u8; 32])); + let root1 = H256::from_slice(&[1u8; 32]); let invalid_votes = vec![true; TEST_VALIDATOR_COUNT - 1]; let mut justifications = std::collections::BTreeMap::new(); @@ -181,30 +179,30 @@ fn test_with_justifications_invalid_length() { #[case::empty_justifications(std::collections::BTreeMap::new())] #[case::single_root({ let mut map = std::collections::BTreeMap::new(); - map.insert(Bytes32(ssz::H256::from_slice(&[1u8; 32])), create_attestations(&[0])); + map.insert(H256::from_slice(&[1u8; 32]), create_attestations(&[0])); map })] #[case::multiple_roots_sorted({ let mut map = std::collections::BTreeMap::new(); - map.insert(Bytes32(ssz::H256::from_slice(&[1u8; 32])), create_attestations(&[0])); - map.insert(Bytes32(ssz::H256::from_slice(&[2u8; 32])), create_attestations(&[1, 2])); + map.insert(H256::from_slice(&[1u8; 32]), create_attestations(&[0])); + map.insert(H256::from_slice(&[2u8; 32]), create_attestations(&[1, 2])); map })] #[case::multiple_roots_unsorted({ let mut map = std::collections::BTreeMap::new(); - map.insert(Bytes32(ssz::H256::from_slice(&[2u8; 32])), create_attestations(&[1, 2])); - map.insert(Bytes32(ssz::H256::from_slice(&[1u8; 32])), create_attestations(&[0])); + map.insert(H256::from_slice(&[2u8; 32]), create_attestations(&[1, 2])); + map.insert(H256::from_slice(&[1u8; 32]), create_attestations(&[0])); map })] #[case::complex_unsorted({ let mut map = std::collections::BTreeMap::new(); - map.insert(Bytes32(ssz::H256::from_slice(&[3u8; 32])), vec![true; TEST_VALIDATOR_COUNT]); - map.insert(Bytes32(ssz::H256::from_slice(&[1u8; 32])), create_attestations(&[0])); - map.insert(Bytes32(ssz::H256::from_slice(&[2u8; 32])), create_attestations(&[1, 2])); + map.insert(H256::from_slice(&[3u8; 32]), vec![true; TEST_VALIDATOR_COUNT]); + map.insert(H256::from_slice(&[1u8; 32]), create_attestations(&[0])); + map.insert(H256::from_slice(&[2u8; 32]), create_attestations(&[1, 2])); map })] fn test_justifications_roundtrip( - #[case] justifications_map: std::collections::BTreeMap>, + #[case] justifications_map: std::collections::BTreeMap>, ) { let state = state(sample_config()); diff --git a/lean_client/containers/tests/unit_tests/state_process.rs b/lean_client/containers/tests/unit_tests/state_process.rs index 6d93223..a1be9e5 100644 --- a/lean_client/containers/tests/unit_tests/state_process.rs +++ b/lean_client/containers/tests/unit_tests/state_process.rs @@ -1,16 +1,8 @@ +use containers::{Slot, State}; // tests/state_process.rs -use containers::{ - block::{hash_tree_root, Block, BlockBody}, - checkpoint::Checkpoint, - slot::Slot, - state::State, - types::{Bytes32, Uint64, ValidatorIndex}, - Attestation, AttestationData, -}; use pretty_assertions::assert_eq; use rstest::{fixture, rstest}; -use ssz::PersistentList as List; -use typenum::U4096; +use ssz::{H256, PersistentList as List, SszHash}; #[path = "common.rs"] mod common; @@ -19,20 +11,17 @@ use common::{create_block, sample_config}; #[fixture] pub fn genesis_state() -> State { let config = sample_config(); - State::generate_genesis(Uint64(config.genesis_time), Uint64(10)) + State::generate_genesis(config.genesis_time, 10) } #[test] fn test_process_slot() { let genesis_state = genesis_state(); - assert_eq!( - genesis_state.latest_block_header.state_root, - Bytes32(ssz::H256::zero()) - ); + assert_eq!(genesis_state.latest_block_header.state_root, H256::zero()); let state_after_slot = genesis_state.process_slot(); - let expected_root = hash_tree_root(&genesis_state); + let expected_root = genesis_state.hash_tree_root(); assert_eq!( state_after_slot.latest_block_header.state_root, @@ -56,7 +45,7 @@ fn test_process_slots() { assert_eq!(new_state.slot, target_slot); assert_eq!( new_state.latest_block_header.state_root, - hash_tree_root(&genesis_state) + genesis_state.hash_tree_root() ); } @@ -73,7 +62,7 @@ fn test_process_slots_backwards() { fn test_process_block_header_valid() { let genesis_state = genesis_state(); let mut state_at_slot_1 = genesis_state.process_slots(Slot(1)).unwrap(); - let genesis_header_root = hash_tree_root(&state_at_slot_1.latest_block_header); + let genesis_header_root = state_at_slot_1.latest_block_header.hash_tree_root(); let block = create_block(1, &mut state_at_slot_1.latest_block_header, None).message; let new_state = state_at_slot_1.process_block_header(&block.block).unwrap(); @@ -96,32 +85,31 @@ fn test_process_block_header_valid() { // Slot 1 is NOT justified yet (no attestations have been processed) assert_eq!(justified_slot_1_relative, false); assert_eq!(new_state.latest_block_header.slot, Slot(1)); - assert_eq!( - new_state.latest_block_header.state_root, - Bytes32(ssz::H256::zero()) - ); + assert_eq!(new_state.latest_block_header.state_root, H256::zero()); } #[rstest] -#[case(2, 1, None, "Block slot mismatch")] -#[case(1, 2, None, "Incorrect block proposer")] -#[case(1, 1, Some(Bytes32(ssz::H256::from_slice(&[0xde; 32]))), "Block parent root mismatch")] +#[case(2, 1, None, "block slot mismatch")] +#[case(1, 2, None, "incorrect block proposer")] +#[case(1, 1, Some(H256::from_slice(&[0xde; 32])), "block parent root mismatch")] fn test_process_block_header_invalid( #[case] bad_slot: u64, #[case] bad_proposer: u64, - #[case] bad_parent_root: Option, + #[case] bad_parent_root: Option, #[case] expected_error: &str, ) { + use containers::{Block, BlockBody}; + let genesis_state = genesis_state(); let state_at_slot_1 = genesis_state.process_slots(Slot(1)).unwrap(); let parent_header = &state_at_slot_1.latest_block_header; - let parent_root = hash_tree_root(parent_header); + let parent_root = parent_header.hash_tree_root(); let block = Block { slot: Slot(bad_slot), - proposer_index: ValidatorIndex(bad_proposer), + proposer_index: bad_proposer, parent_root: bad_parent_root.unwrap_or(parent_root), - state_root: Bytes32(ssz::H256::zero()), + state_root: H256::zero(), body: BlockBody { attestations: List::default(), }, @@ -130,6 +118,9 @@ fn test_process_block_header_invalid( let result = state_at_slot_1.process_block_header(&block); assert!(result.is_err()); - let err_msg = result.unwrap_err(); - assert!(err_msg.contains(expected_error)); + let err_msg = format!("{:?}", result.unwrap_err()); + assert!( + err_msg.contains(expected_error), + r#"Expected to receive "{expected_error}", but received "{err_msg}" instead."# + ); } diff --git a/lean_client/containers/tests/unit_tests/state_transition.rs b/lean_client/containers/tests/unit_tests/state_transition.rs index bcf0ea1..f638bbb 100644 --- a/lean_client/containers/tests/unit_tests/state_transition.rs +++ b/lean_client/containers/tests/unit_tests/state_transition.rs @@ -5,25 +5,22 @@ // tests/state_transition.rs use containers::{ - block::{ - hash_tree_root, Block, BlockSignatures, BlockWithAttestation, SignedBlockWithAttestation, - }, - state::State, - types::{Bytes32, Uint64}, - Attestation, Signature, Slot, + Attestation, Block, BlockSignatures, BlockWithAttestation, SignedBlockWithAttestation, Slot, + State, }; use pretty_assertions::assert_eq; use rstest::fixture; -use ssz::PersistentList; +use ssz::{H256, PersistentList, SszHash}; #[path = "common.rs"] mod common; use common::{create_block, sample_config}; +use xmss::Signature; #[fixture] fn genesis_state() -> State { let config = sample_config(); - State::generate_genesis(Uint64(config.genesis_time), Uint64(4)) + State::generate_genesis(config.genesis_time, 4) } #[test] @@ -41,7 +38,7 @@ fn test_state_transition_full() { let expected_state = state_after_header.process_attestations(&block.body.attestations); let block_with_correct_root = Block { - state_root: hash_tree_root(&expected_state), + state_root: expected_state.hash_tree_root(), ..block }; @@ -57,7 +54,10 @@ fn test_state_transition_full() { .state_transition(final_signed_block_with_attestation, true) .unwrap(); - assert_eq!(final_state, expected_state); + assert_eq!( + final_state.hash_tree_root(), + expected_state.hash_tree_root() + ); } #[test] @@ -75,7 +75,7 @@ fn test_state_transition_invalid_signatures() { let expected_state = state_after_header.process_attestations(&block.body.attestations); let block_with_correct_root = Block { - state_root: hash_tree_root(&expected_state), + state_root: expected_state.hash_tree_root(), ..block }; @@ -89,7 +89,6 @@ fn test_state_transition_invalid_signatures() { let result = state.state_transition(final_signed_block_with_attestation, false); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "Block signatures must be valid"); } // Test with bad state root using devnet2 BlockSignatures structure @@ -102,7 +101,7 @@ fn test_state_transition_bad_state_root() { create_block(1, &mut state_at_slot_1.latest_block_header, None); let mut block = signed_block_with_attestation.message.block.clone(); - block.state_root = Bytes32(ssz::H256::zero()); + block.state_root = H256::zero(); let final_signed_block_with_attestation = SignedBlockWithAttestation { message: BlockWithAttestation { @@ -117,7 +116,6 @@ fn test_state_transition_bad_state_root() { let result = state.state_transition(final_signed_block_with_attestation, true); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "Invalid block state root"); } #[test] @@ -137,7 +135,7 @@ fn test_state_transition_devnet2() { // Ensure the state root matches the expected state let block_with_correct_root = Block { - state_root: hash_tree_root(&expected_state), + state_root: expected_state.hash_tree_root(), ..block }; @@ -154,5 +152,8 @@ fn test_state_transition_devnet2() { .state_transition(final_signed_block_with_attestation, true) .unwrap(); - assert_eq!(final_state, expected_state); + assert_eq!( + final_state.hash_tree_root(), + expected_state.hash_tree_root() + ); } diff --git a/lean_client/fork_choice/Cargo.toml b/lean_client/fork_choice/Cargo.toml index f503590..59fb10a 100644 --- a/lean_client/fork_choice/Cargo.toml +++ b/lean_client/fork_choice/Cargo.toml @@ -1,18 +1,18 @@ [package] -name = "fork-choice" -version = "0.1.0" -edition = "2021" - -[features] -default = [] +name = "fork_choice" +edition = { workspace = true } [dependencies] -env-config = { path = "../env-config", default-features = false } -containers = { path = "../containers", default-features = false } +anyhow = { workspace = true } +containers = { workspace = true } +env-config = { workspace = true } ssz = { workspace = true } -tracing = "0.1" +tracing = { workspace = true } +xmss = { workspace = true } [dev-dependencies] -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } +rand = { workspace = true } +rand_chacha = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } test-generator = { workspace = true } diff --git a/lean_client/fork_choice/src/handlers.rs b/lean_client/fork_choice/src/handlers.rs index 353dca2..73c5224 100644 --- a/lean_client/fork_choice/src/handlers.rs +++ b/lean_client/fork_choice/src/handlers.rs @@ -1,11 +1,8 @@ -use crate::store::*; -use containers::attestation::AttestationData; -use containers::SignatureKey; -use containers::{ - attestation::SignedAttestation, block::SignedBlockWithAttestation, Bytes32, ValidatorIndex, -}; -use ssz::SszHash; -use tracing::{debug, info}; +use anyhow::{Result, anyhow, bail, ensure}; +use containers::{AttestationData, SignatureKey, SignedAttestation, SignedBlockWithAttestation}; +use ssz::{H256, SszHash}; + +use crate::store::{INTERVALS_PER_SLOT, SECONDS_PER_INTERVAL, Store, tick_interval, update_head}; #[inline] pub fn on_tick(store: &mut Store, time: u64, has_proposal: bool) { @@ -26,62 +23,63 @@ pub fn on_tick(store: &mut Store, time: u64, has_proposal: bool) { /// 2. A vote cannot span backwards in time (source > target). /// 3. A vote cannot be for a future slot. /// 4. Checkpoint slots must match block slots. -fn validate_attestation_data(store: &Store, data: &AttestationData) -> Result<(), String> { +fn validate_attestation_data(store: &Store, data: &AttestationData) -> Result<()> { // Cannot count a vote if we haven't seen the blocks involved - if !store.blocks.contains_key(&data.source.root) { - return Err(format!( - "Unknown source block: {:?}", - &data.source.root.0.as_bytes()[..8] - )); - } - if !store.blocks.contains_key(&data.target.root) { - return Err(format!( - "Unknown target block: {:?}", - &data.target.root.0.as_bytes()[..8] - )); - } - if !store.blocks.contains_key(&data.head.root) { - return Err(format!( - "Unknown head block: {:?}", - &data.head.root.0.as_bytes()[..8] - )); - } + ensure!( + store.blocks.contains_key(&data.source.root), + "Unknown source block: {:?}", + data.source.root + ); + + ensure!( + store.blocks.contains_key(&data.target.root), + "Unknown target block: {:?}", + &data.target.root + ); + + ensure!( + store.blocks.contains_key(&data.head.root), + "Unknown head block: {:?}", + &data.head.root + ); // Source must be older than Target. - if data.source.slot > data.target.slot { - return Err(format!( - "Source checkpoint slot {} must not exceed target slot {}", - data.source.slot.0, data.target.slot.0 - )); - } + ensure!( + data.source.slot <= data.target.slot, + "Source checkpoint slot {} must not exceed target slot {}", + data.source.slot.0, + data.target.slot.0 + ); // Validate checkpoint slots match block slots // Per devnet-2, store.blocks now contains Block (not SignedBlockWithAttestation) let source_block = &store.blocks[&data.source.root]; let target_block = &store.blocks[&data.target.root]; - if source_block.slot != data.source.slot { - return Err(format!( - "Source checkpoint slot mismatch: checkpoint {} vs block {}", - data.source.slot.0, source_block.slot.0 - )); - } - if target_block.slot != data.target.slot { - return Err(format!( - "Target checkpoint slot mismatch: checkpoint {} vs block {}", - data.target.slot.0, target_block.slot.0 - )); - } + ensure!( + source_block.slot == data.source.slot, + "Source checkpoint slot mismatch: checkpoint {} vs block {}", + data.source.slot.0, + source_block.slot.0 + ); + + ensure!( + target_block.slot == data.target.slot, + "Target checkpoint slot mismatch: checkpoint {} vs block {}", + data.target.slot.0, + target_block.slot.0 + ); // Validate attestation is not too far in the future // We allow a small margin for clock disparity (1 slot), but no further. let current_slot = store.time / INTERVALS_PER_SLOT; - if data.slot.0 > current_slot + 1 { - return Err(format!( - "Attestation too far in future: attestation slot {} > current slot {} + 1", - data.slot.0, current_slot - )); - } + + ensure!( + data.slot.0 <= current_slot + 1, + "Attestation too far in future: attestation slot {} > current slot {} + 1", + data.slot.0, + current_slot + ); Ok(()) } @@ -96,15 +94,15 @@ fn validate_attestation_data(store: &Store, data: &AttestationData) -> Result<() pub fn on_gossip_attestation( store: &mut Store, signed_attestation: SignedAttestation, -) -> Result<(), String> { - let validator_id = ValidatorIndex(signed_attestation.validator_id); +) -> Result<()> { + let validator_id = signed_attestation.validator_id; let attestation_data = signed_attestation.message.clone(); // Validate the attestation data first validate_attestation_data(store, &attestation_data)?; // Store signature for later lookup during block building - let data_root = attestation_data.data_root_bytes(); + let data_root = attestation_data.hash_tree_root(); let sig_key = SignatureKey::new(signed_attestation.validator_id, data_root); store .gossip_signatures @@ -127,8 +125,8 @@ pub fn on_attestation( store: &mut Store, signed_attestation: SignedAttestation, is_from_block: bool, -) -> Result<(), String> { - let validator_id = ValidatorIndex(signed_attestation.validator_id); +) -> Result<()> { + let validator_id = signed_attestation.validator_id; let attestation_data = signed_attestation.message.clone(); // Validate attestation data @@ -136,7 +134,7 @@ pub fn on_attestation( if !is_from_block { // Store signature for later aggregation during block building - let data_root = attestation_data.data_root_bytes(); + let data_root = attestation_data.hash_tree_root(); let sig_key = SignatureKey::new(signed_attestation.validator_id, data_root); store .gossip_signatures @@ -149,10 +147,10 @@ pub fn on_attestation( /// Internal attestation processing - stores AttestationData fn on_attestation_internal( store: &mut Store, - validator_id: ValidatorIndex, + validator_id: u64, attestation_data: AttestationData, is_from_block: bool, -) -> Result<(), String> { +) -> Result<()> { let attestation_slot = attestation_data.slot; if is_from_block { @@ -195,8 +193,8 @@ fn on_attestation_internal( /// 3. Processing attestations included in the block body (on-chain) /// 4. Updating the forkchoice head /// 5. Processing the proposer's attestation (as if gossiped) -pub fn on_block(store: &mut Store, signed_block: SignedBlockWithAttestation) -> Result<(), String> { - let block_root = containers::block::compute_block_root(&signed_block.message.block); +pub fn on_block(store: &mut Store, signed_block: SignedBlockWithAttestation) -> Result<()> { + let block_root = signed_block.message.block.hash_tree_root(); if store.blocks.contains_key(&block_root) { return Ok(()); @@ -204,17 +202,17 @@ pub fn on_block(store: &mut Store, signed_block: SignedBlockWithAttestation) -> let parent_root = signed_block.message.block.parent_root; - if !store.states.contains_key(&parent_root) && !parent_root.0.is_zero() { + if !store.states.contains_key(&parent_root) && !parent_root.is_zero() { store .blocks_queue .entry(parent_root) .or_insert_with(Vec::new) .push(signed_block); - return Err(format!( + bail!( "Err: (Fork-choice::Handlers::OnBlock) Block queued: parent {:?} not yet available (pending: {} blocks)", - &parent_root.0.as_bytes()[..4], + &parent_root.as_bytes()[..4], store.blocks_queue.values().map(|v| v.len()).sum::() - )); + ); } process_block_internal(store, signed_block, block_root)?; @@ -226,20 +224,16 @@ pub fn on_block(store: &mut Store, signed_block: SignedBlockWithAttestation) -> fn process_block_internal( store: &mut Store, signed_block: SignedBlockWithAttestation, - block_root: Bytes32, -) -> Result<(), String> { + block_root: H256, +) -> Result<()> { let block = signed_block.message.block.clone(); let attestations_count = block.body.attestations.len_u64(); // Get parent state for validation - let state = match store.states.get(&block.parent_root) { - Some(state) => state, - None => { - return Err( - "Err: (Fork-choice::Handlers::ProcessBlockInternal) No parent state.".to_string(), - ); - } - }; + let state = store + .states + .get(&block.parent_root) + .ok_or(anyhow!("no parent state"))?; // Debug: Log parent state checkpoints before transition tracing::debug!( @@ -306,7 +300,7 @@ fn process_block_internal( // Store aggregated proofs for future block building // Each attestation_signature proof is indexed by (validator_id, data_root) for each participating validator for (att_idx, aggregated_attestation) in aggregated_attestations.into_iter().enumerate() { - let data_root = aggregated_attestation.data.data_root_bytes(); + let data_root = aggregated_attestation.data.hash_tree_root(); // Get the corresponding proof from attestation_signatures if let Ok(proof_data) = signatures.attestation_signatures.get(att_idx as u64) { @@ -342,7 +336,7 @@ fn process_block_internal( for validator_id in validator_ids { on_attestation_internal( store, - ValidatorIndex(validator_id), + validator_id, aggregated_attestation.data.clone(), true, // is_from_block )?; @@ -353,9 +347,8 @@ fn process_block_internal( update_head(store); // Store proposer's signature for later block building - let proposer_data_root = proposer_attestation.data.data_root_bytes(); - let proposer_sig_key = - SignatureKey::new(proposer_attestation.validator_id.0, proposer_data_root); + let proposer_data_root = proposer_attestation.data.hash_tree_root(); + let proposer_sig_key = SignatureKey::new(proposer_attestation.validator_id, proposer_data_root); store .gossip_signatures .insert(proposer_sig_key, signed_block.signature.proposer_signature); @@ -364,7 +357,7 @@ fn process_block_internal( // This ensures it goes to "new" attestations and doesn't immediately affect fork choice on_attestation_internal( store, - ValidatorIndex(proposer_attestation.validator_id.0), + proposer_attestation.validator_id, proposer_attestation.data.clone(), false, // is_from_block )?; @@ -372,11 +365,11 @@ fn process_block_internal( Ok(()) } -fn process_pending_blocks(store: &mut Store, mut roots: Vec) { +fn process_pending_blocks(store: &mut Store, mut roots: Vec) { while let Some(parent_root) = roots.pop() { if let Some(purgatory) = store.blocks_queue.remove(&parent_root) { for block in purgatory { - let block_origins = containers::block::compute_block_root(&block.message.block); + let block_origins = block.message.block.hash_tree_root(); if let Ok(()) = process_block_internal(store, block, block_origins) { roots.push(block_origins); } diff --git a/lean_client/fork_choice/src/store.rs b/lean_client/fork_choice/src/store.rs index f1b1e51..39e08a7 100644 --- a/lean_client/fork_choice/src/store.rs +++ b/lean_client/fork_choice/src/store.rs @@ -1,14 +1,12 @@ +use std::collections::HashMap; + +use anyhow::{Error, Result, anyhow, ensure}; use containers::{ - attestation::{AttestationData, SignedAttestation}, - block::{Block, SignedBlockWithAttestation}, - checkpoint::Checkpoint, - config::Config, - state::State, - Bytes32, Root, Slot, ValidatorIndex, + AggregatedSignatureProof, Attestation, AttestationData, Block, Checkpoint, Config, + SignatureKey, SignedBlockWithAttestation, Slot, State, }; -use containers::{AggregatedSignatureProof, Signature, SignatureKey}; -use ssz::SszHash; -use std::collections::HashMap; +use ssz::{H256, SszHash}; +use xmss::Signature; pub type Interval = u64; pub const INTERVALS_PER_SLOT: Interval = 4; @@ -23,23 +21,23 @@ pub struct Store { pub config: Config, - pub head: Root, + pub head: H256, - pub safe_target: Root, + pub safe_target: H256, pub latest_justified: Checkpoint, pub latest_finalized: Checkpoint, - pub blocks: HashMap, + pub blocks: HashMap, - pub states: HashMap, + pub states: HashMap, - pub latest_known_attestations: HashMap, + pub latest_known_attestations: HashMap, - pub latest_new_attestations: HashMap, + pub latest_new_attestations: HashMap, - pub blocks_queue: HashMap>, + pub blocks_queue: HashMap>, pub gossip_signatures: HashMap, @@ -57,9 +55,9 @@ pub fn get_forkchoice_store( let block_slot = block.slot; // Compute block root using the header hash (canonical block root) - let block_root = containers::block::compute_block_root(&block); + let block_root = block.hash_tree_root(); - let latest_justified = if anchor_state.latest_justified.root.0.is_zero() { + let latest_justified = if anchor_state.latest_justified.root.is_zero() { Checkpoint { root: block_root, slot: block_slot, @@ -68,7 +66,7 @@ pub fn get_forkchoice_store( anchor_state.latest_justified.clone() }; - let latest_finalized = if anchor_state.latest_finalized.root.0.is_zero() { + let latest_finalized = if anchor_state.latest_finalized.root.is_zero() { Checkpoint { root: block_root, slot: block_slot, @@ -99,11 +97,11 @@ pub fn get_forkchoice_store( pub fn get_fork_choice_head( store: &Store, - mut root: Root, - latest_attestations: &HashMap, + mut root: H256, + latest_attestations: &HashMap, min_votes: usize, -) -> Root { - if root.0.is_zero() { +) -> H256 { + if root.is_zero() { root = store .blocks .iter() @@ -111,7 +109,7 @@ pub fn get_fork_choice_head( .map(|(r, _)| *r) .expect("Error: Empty block."); } - let mut vote_weights: HashMap = HashMap::new(); + let mut vote_weights: HashMap = HashMap::new(); let root_slot = store.blocks[&root].slot; // stage 1: accumulate weights by walking up from each attestation's head @@ -126,7 +124,7 @@ pub fn get_fork_choice_head( if let Some(parent_block) = store.blocks.get(&curr) { curr = parent_block.parent_root; - if curr.0.is_zero() { + if curr.is_zero() { break; } if let Some(next_block) = store.blocks.get(&curr) { @@ -142,9 +140,9 @@ pub fn get_fork_choice_head( } // stage 2: build adjacency tree (parent -> children) - let mut child_map: HashMap> = HashMap::new(); + let mut child_map: HashMap> = HashMap::new(); for (block_hash, block) in &store.blocks { - if !block.parent_root.0.is_zero() { + if !block.parent_root.is_zero() { if vote_weights.get(block_hash).copied().unwrap_or(0) >= min_votes { child_map .entry(block.parent_root) @@ -174,7 +172,7 @@ pub fn get_fork_choice_head( } } -pub fn get_latest_justified(states: &HashMap) -> Option<&Checkpoint> { +pub fn get_latest_justified(states: &HashMap) -> Option<&Checkpoint> { states .values() .map(|state| &state.latest_justified) @@ -257,7 +255,7 @@ pub fn get_vote_target(store: &Store) -> Checkpoint { } #[inline] -pub fn get_proposal_head(store: &mut Store, slot: Slot) -> Root { +pub fn get_proposal_head(store: &mut Store, slot: Slot) -> H256 { let slot_time = store.config.genesis_time + (slot.0 * SECONDS_PER_SLOT); crate::handlers::on_tick(store, slot_time, true); @@ -286,34 +284,26 @@ pub fn get_proposal_head(store: &mut Store, slot: Slot) -> Root { pub fn produce_block_with_signatures( store: &mut Store, slot: Slot, - validator_index: ValidatorIndex, -) -> Result< - ( - Root, - containers::block::Block, - Vec, - ), - String, -> { - use containers::Attestation; - + validator_index: u64, +) -> Result<(H256, Block, Vec)> { // Get parent block head let head_root = get_proposal_head(store, slot); let head_state = store .states .get(&head_root) - .ok_or_else(|| "Head state not found".to_string())? + .ok_or_else(|| anyhow!("Head state not found"))? .clone(); // Validate proposer authorization for this slot let num_validators = head_state.validators.len_u64(); let expected_proposer = slot.0 % num_validators; - if validator_index.0 != expected_proposer { - return Err(format!( - "Validator {} is not the proposer for slot {} (expected {})", - validator_index.0, slot.0, expected_proposer - )); - } + ensure!( + validator_index == expected_proposer, + "Validator {} is not the proposer for slot {} (expected {})", + validator_index, + slot.0, + expected_proposer + ); // Convert AttestationData to Attestation objects for build_block // Per devnet-2, store now holds AttestationData directly @@ -321,14 +311,13 @@ pub fn produce_block_with_signatures( .latest_known_attestations .iter() .map(|(validator_idx, attestation_data)| Attestation { - validator_id: containers::Uint64(validator_idx.0), + validator_id: *validator_idx, data: attestation_data.clone(), }) .collect(); // Get known block roots for attestation validation - let known_block_roots: std::collections::HashSet = - store.blocks.keys().copied().collect(); + let known_block_roots: std::collections::HashSet = store.blocks.keys().copied().collect(); // Build block with fixed-point attestation collection and signature aggregation let (final_block, final_post_state, _aggregated_attestations, signatures) = head_state @@ -344,7 +333,7 @@ pub fn produce_block_with_signatures( )?; // Compute block root using the header hash (canonical block root) - let block_root = containers::block::compute_block_root(&final_block); + let block_root = final_block.hash_tree_root(); // Store block and state (per devnet-2, we store the plain Block) store.blocks.insert(block_root, final_block.clone()); diff --git a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs index cd19be7..016d35f 100644 --- a/lean_client/fork_choice/tests/fork_choice_test_vectors.rs +++ b/lean_client/fork_choice/tests/fork_choice_test_vectors.rs @@ -1,27 +1,20 @@ +use containers::{ + AggregatedAttestation, AggregationBits, Attestation, AttestationData, Block, BlockBody, + BlockHeader, BlockSignatures, BlockWithAttestation, Checkpoint, Config, HistoricalBlockHashes, + JustificationRoots, JustificationValidators, JustifiedSlots, SignedBlockWithAttestation, Slot, + State, Validator, Validators, +}; use fork_choice::{ handlers::{on_block, on_tick}, - store::{get_forkchoice_store, Store}, -}; - -use containers::{ - attestation::{Attestation, AttestationData}, - block::{ - hash_tree_root, Block, BlockBody, BlockHeader, BlockSignatures, BlockWithAttestation, - SignedBlockWithAttestation, - }, - checkpoint::Checkpoint, - config::Config, - public_key::PublicKey, - state::State, - AggregatedAttestation, AggregationBits, Bytes32, HistoricalBlockHashes, JustificationRoots, - JustificationsValidators, JustifiedSlots, Signature, Slot, Uint64, ValidatorIndex, Validators, + store::{Store, get_forkchoice_store}, }; use serde::Deserialize; -use ssz::{SszHash, H256}; +use ssz::{H256, SszHash}; use std::{collections::HashMap, fs::File}; use std::{panic::AssertUnwindSafe, path::Path}; use test_generator::test_resources; +use xmss::PublicKey; #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -82,7 +75,7 @@ impl Into for TestAnchorState { } let mut justifications_validators = - JustificationsValidators::new(false, self.justifications_validators.data.len()); + JustificationValidators::new(false, self.justifications_validators.data.len()); for (i, &val) in self.justifications_validators.data.iter().enumerate() { if val { justifications_validators.set(i, true); @@ -91,11 +84,13 @@ impl Into for TestAnchorState { let mut validators = Validators::default(); for test_validator in &self.validators.data { - let pubkey = PublicKey::from_hex(&test_validator.pubkey) + let pubkey: PublicKey = test_validator + .pubkey + .parse() .expect("Failed to parse validator pubkey"); - let validator = containers::validator::Validator { + let validator = Validator { pubkey, - index: containers::Uint64(test_validator.index), + index: test_validator.index, }; validators.push(validator).expect("Failed to add validator"); } @@ -143,7 +138,7 @@ impl Into for TestBlockHeader { fn into(self) -> BlockHeader { BlockHeader { slot: Slot(self.slot), - proposer_index: ValidatorIndex(self.proposer_index), + proposer_index: self.proposer_index, parent_root: parse_root(&self.parent_root), state_root: parse_root(&self.state_root), body_root: parse_root(&self.body_root), @@ -202,7 +197,7 @@ impl Into for TestAnchorBlock { let block = Block { slot: Slot(self.slot), - proposer_index: ValidatorIndex(self.proposer_index), + proposer_index: self.proposer_index, parent_root: parse_root(&self.parent_root), state_root: parse_root(&self.state_root), body: BlockBody { attestations }, @@ -210,7 +205,7 @@ impl Into for TestAnchorBlock { // Create proposer attestation let proposer_attestation = Attestation { - validator_id: Uint64(self.proposer_index), + validator_id: self.proposer_index, data: AttestationData { slot: Slot(self.slot), head: Checkpoint { @@ -252,7 +247,7 @@ impl Into for TestBlock { fn into(self) -> Block { Block { slot: Slot(self.slot), - proposer_index: ValidatorIndex(self.proposer_index), + proposer_index: self.proposer_index, parent_root: parse_root(&self.parent_root), state_root: parse_root(&self.state_root), body: self.body.into(), @@ -288,7 +283,7 @@ struct TestAttestation { impl Into for TestAttestation { fn into(self) -> Attestation { Attestation { - validator_id: Uint64(self.validator_id), + validator_id: self.validator_id, data: self.data.into(), } } @@ -415,7 +410,7 @@ struct TestInfo { fixture_format: String, } -fn parse_root(hex_str: &str) -> Bytes32 { +fn parse_root(hex_str: &str) -> H256 { let hex = hex_str.trim_start_matches("0x"); let mut bytes = [0u8; 32]; @@ -428,13 +423,13 @@ fn parse_root(hex_str: &str) -> Bytes32 { panic!("Invalid root length: {} (expected 64 hex chars)", hex.len()); } - Bytes32(ssz::H256::from(bytes)) + H256::from(bytes) } fn verify_checks( store: &Store, checks: &Option, - block_labels: &HashMap, + block_labels: &HashMap, step_idx: usize, ) -> Result<(), String> { // If no checks provided, nothing to verify @@ -468,15 +463,19 @@ fn verify_checks( .unwrap_or(0); return Err(format!( "Step {}: Head root mismatch for label '{}' - expected slot {}, got slot {} (known_attestations: {}, new_attestations: {})", - step_idx, label, expected_slot, actual_slot, - store.latest_known_attestations.len(), store.latest_new_attestations.len() + step_idx, + label, + expected_slot, + actual_slot, + store.latest_known_attestations.len(), + store.latest_new_attestations.len() )); } } if let Some(att_checks) = &checks.attestation_checks { for check in att_checks { - let validator = ValidatorIndex(check.validator); + let validator = check.validator; match check.location.as_str() { "new" => { @@ -492,7 +491,10 @@ fn verify_checks( if attestation_data.target.slot.0 != target_slot { return Err(format!( "Step {}: Validator {} new attestation target slot mismatch - expected {}, got {}", - step_idx, check.validator, target_slot, attestation_data.target.slot.0 + step_idx, + check.validator, + target_slot, + attestation_data.target.slot.0 )); } } @@ -535,7 +537,7 @@ fn forkchoice(spec_file: &str) { let mut anchor_state: State = case.anchor_state.into(); let anchor_block: SignedBlockWithAttestation = case.anchor_block.into(); - let body_root = hash_tree_root(&anchor_block.message.block.body); + let body_root = anchor_block.message.block.body.hash_tree_root(); anchor_state.latest_block_header = BlockHeader { slot: anchor_block.message.block.slot, proposer_index: anchor_block.message.block.proposer_index, @@ -545,7 +547,7 @@ fn forkchoice(spec_file: &str) { }; let mut store = get_forkchoice_store(anchor_state, anchor_block, config); - let mut block_labels: HashMap = HashMap::new(); + let mut block_labels: HashMap = HashMap::new(); for (step_idx, step) in case.steps.into_iter().enumerate() { match step.step_type.as_str() { @@ -562,8 +564,7 @@ fn forkchoice(spec_file: &str) { message: block, signature: BlockSignatures::default(), }; - let block_root = - containers::block::compute_block_root(&signed_block.message.block); + let block_root = signed_block.message.block.hash_tree_root(); // Advance time to the block's slot to ensure attestations are processable // SECONDS_PER_SLOT is 4 (not 12) @@ -571,7 +572,7 @@ fn forkchoice(spec_file: &str) { store.config.genesis_time + (signed_block.message.block.slot.0 * 4); on_tick(&mut store, block_time, false); - on_block(&mut store, signed_block)?; + on_block(&mut store, signed_block).unwrap(); Ok(block_root) })); diff --git a/lean_client/fork_choice/tests/unit_tests/common.rs b/lean_client/fork_choice/tests/unit_tests/common.rs index dc1a762..661f044 100644 --- a/lean_client/fork_choice/tests/unit_tests/common.rs +++ b/lean_client/fork_choice/tests/unit_tests/common.rs @@ -1,26 +1,22 @@ use containers::{ - attestation::Attestation, - block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, - config::Config, - state::State, - validator::Validator, - Bytes32, Slot, Uint64, ValidatorIndex, + Attestation, Block, BlockBody, BlockWithAttestation, Config, SignedBlockWithAttestation, Slot, + State, Validator, }; -use fork_choice::store::{get_forkchoice_store, Store}; -use ssz::SszHash; +use fork_choice::store::{Store, get_forkchoice_store}; +use ssz::{H256, SszHash}; pub fn create_test_store() -> Store { let config = Config { genesis_time: 1000 }; let validators = vec![Validator::default(); 10]; - let state = State::generate_genesis_with_validators(Uint64(1000), validators); + let state = State::generate_genesis_with_validators(1000, validators); let block = Block { slot: Slot(0), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32::default(), - state_root: Bytes32(state.hash_tree_root()), + proposer_index: 0, + parent_root: H256::default(), + state_root: state.hash_tree_root(), body: BlockBody::default(), }; diff --git a/lean_client/fork_choice/tests/unit_tests/fork_choice.rs b/lean_client/fork_choice/tests/unit_tests/fork_choice.rs index 068f716..ee3f321 100644 --- a/lean_client/fork_choice/tests/unit_tests/fork_choice.rs +++ b/lean_client/fork_choice/tests/unit_tests/fork_choice.rs @@ -1,6 +1,7 @@ use super::common::create_test_store; -use containers::Slot; +use containers::{Block, BlockBody, Slot}; use fork_choice::store::{get_proposal_head, get_vote_target}; +use ssz::{H256, SszHash}; #[test] fn test_get_proposal_head_basic() { @@ -22,12 +23,6 @@ fn test_get_proposal_head_advances_time() { #[test] fn test_get_vote_target_chain() { - use containers::{ - block::{Block, BlockBody}, - Bytes32, ValidatorIndex, - }; - use ssz::SszHash; - let mut store = create_test_store(); let mut parent_root = store.head; @@ -36,13 +31,13 @@ fn test_get_vote_target_chain() { for i in 1..=10 { let block = Block { slot: Slot(i), - proposer_index: ValidatorIndex(0), + proposer_index: 0, parent_root, - state_root: Bytes32::default(), + state_root: H256::default(), body: BlockBody::default(), }; - let block_root = containers::block::compute_block_root(&block); + let block_root = block.hash_tree_root(); // Insert Block directly per leanSpec store.blocks.insert(block_root, block); diff --git a/lean_client/fork_choice/tests/unit_tests/time.rs b/lean_client/fork_choice/tests/unit_tests/time.rs index ff99491..a4db1b5 100644 --- a/lean_client/fork_choice/tests/unit_tests/time.rs +++ b/lean_client/fork_choice/tests/unit_tests/time.rs @@ -1,7 +1,6 @@ use super::common::create_test_store; -use containers::{Slot, Uint64}; use fork_choice::handlers::on_tick; -use fork_choice::store::{tick_interval, INTERVALS_PER_SLOT, SECONDS_PER_SLOT}; +use fork_choice::store::{INTERVALS_PER_SLOT, SECONDS_PER_SLOT, tick_interval}; #[test] fn test_on_tick_basic() { diff --git a/lean_client/fork_choice/tests/unit_tests/validator.rs b/lean_client/fork_choice/tests/unit_tests/validator.rs index dc69cce..ee9a7b4 100644 --- a/lean_client/fork_choice/tests/unit_tests/validator.rs +++ b/lean_client/fork_choice/tests/unit_tests/validator.rs @@ -2,20 +2,55 @@ //! //! Ported from spec/tests/lean_spec/subspecs/forkchoice/test_validator.py -use super::common::create_test_store; +use std::collections::HashMap; + +use crate::unit_tests::common::create_test_store; use containers::{ - attestation::{Attestation, AttestationData}, - block::{Block, BlockBody}, - checkpoint::Checkpoint, - config::Config, - state::State, - validator::Validator, - Bytes32, Signature, SignatureKey, Slot, Uint64, ValidatorIndex, + Attestation, AttestationData, Block, BlockBody, BlockWithAttestation, Checkpoint, Config, + SignatureKey, SignedBlockWithAttestation, Slot, State, Validator, }; use fork_choice::store::{ - get_vote_target, produce_block_with_signatures, update_head, Store, + Store, get_forkchoice_store, get_vote_target, produce_block_with_signatures, update_head, }; -use ssz::SszHash; +use rand::SeedableRng; +use rand_chacha::ChaChaRng; +use ssz::{H256, SszHash}; +use xmss::SecretKey; + +fn create_test_store_with_signers() -> (Store, HashMap) { + let config = Config { genesis_time: 1000 }; + + let mut rng = ChaChaRng::seed_from_u64(1337); + let (validators, keys) = (0..10) + .map(|index| { + let (pubkey, secret_key) = SecretKey::generate_key_pair(&mut rng, 0, 10); + + (Validator { index, pubkey }, (index, secret_key)) + }) + .unzip(); + + let state = State::generate_genesis_with_validators(1000, validators); + + let block = Block { + slot: Slot(0), + proposer_index: 0, + parent_root: H256::default(), + state_root: state.hash_tree_root(), + body: BlockBody::default(), + }; + + let block_with_attestation = BlockWithAttestation { + block: block.clone(), + proposer_attestation: Attestation::default(), + }; + + let signed_block = SignedBlockWithAttestation { + message: block_with_attestation, + signature: Default::default(), + }; + + (get_forkchoice_store(state, signed_block, config), keys) +} /// Build AttestationData matching the current store state for a given slot. /// @@ -35,11 +70,6 @@ fn produce_attestation_data(store: &Store, slot: Slot) -> AttestationData { } } -/// Create a mock XMSS signature (all zeros). -fn make_mock_signature() -> Signature { - Default::default() -} - // --------------------------------------------------------------------------- // TestBlockProduction // --------------------------------------------------------------------------- @@ -50,7 +80,7 @@ fn test_produce_block_basic() { let initial_head = store.head; let slot = Slot(1); - let validator_idx = ValidatorIndex(1); + let validator_idx = 1; let (block_root, block, _signatures) = produce_block_with_signatures(&mut store, slot, validator_idx) @@ -60,7 +90,7 @@ fn test_produce_block_basic() { assert_eq!(block.slot, slot); assert_eq!(block.proposer_index, validator_idx); assert_eq!(block.parent_root, initial_head); - assert_ne!(block.state_root, Bytes32::default()); + assert_ne!(block.state_root, H256::default()); // Verify block was added to store assert!(store.blocks.contains_key(&block_root)); @@ -71,11 +101,11 @@ fn test_produce_block_basic() { fn test_produce_block_unauthorized_proposer() { let mut store = create_test_store(); let slot = Slot(1); - let wrong_validator = ValidatorIndex(2); // Not proposer for slot 1 + let wrong_validator = 2; // Not proposer for slot 1 let result = produce_block_with_signatures(&mut store, slot, wrong_validator); assert!(result.is_err()); - let err = result.unwrap_err(); + let err = format!("{:?}", result.unwrap_err()); assert!( err.contains("is not the proposer for slot"), "unexpected error: {err}" @@ -84,7 +114,7 @@ fn test_produce_block_unauthorized_proposer() { #[test] fn test_produce_block_with_attestations() { - let mut store = create_test_store(); + let (mut store, keys) = create_test_store_with_signers(); let head_block = store.blocks[&store.head].clone(); let head_checkpoint = Checkpoint { root: store.head, @@ -100,25 +130,27 @@ fn test_produce_block_with_attestations() { target: target.clone(), source: store.latest_justified.clone(), }; - store - .latest_known_attestations - .insert(ValidatorIndex(vid), data.clone()); + store.latest_known_attestations.insert(vid, data.clone()); + let data_root = data.hash_tree_root(); let sig_key = SignatureKey { validator_id: vid, - data_root: Bytes32(data.hash_tree_root()), + data_root: data_root.clone(), }; - store - .gossip_signatures - .insert(sig_key, make_mock_signature()); + store.gossip_signatures.insert( + sig_key, + keys.get(&vid) + .unwrap() + .sign(data_root, head_block.slot.0 as u32) + .unwrap(), + ); } let slot = Slot(2); - let validator_idx = ValidatorIndex(2); + let validator_idx = 2; - let (_root, block, signatures) = - produce_block_with_signatures(&mut store, slot, validator_idx) - .expect("block production should succeed"); + let (_root, block, signatures) = produce_block_with_signatures(&mut store, slot, validator_idx) + .expect("block production should succeed"); // Block should include the 2 attestations we added (validators 5 and 6). // Attestations may be aggregated, so check the count matches signatures. @@ -129,7 +161,7 @@ fn test_produce_block_with_attestations() { // Verify block structure is correct assert_eq!(block.slot, slot); assert_eq!(block.proposer_index, validator_idx); - assert_ne!(block.state_root, Bytes32::default()); + assert_ne!(block.state_root, H256::default()); // Verify each aggregated signature proof let head_state = &store.states[&store.head]; @@ -149,13 +181,12 @@ fn test_produce_block_with_attestations() { .get(vid) .expect("validator index out of range") .pubkey + .clone() }) .collect(); - let message: [u8; 32] = agg_att.data.data_root_bytes().0.into(); let epoch = agg_att.data.slot.0 as u32; proof - .proof_data - .verify(&public_keys, &message, epoch) + .verify(public_keys, agg_att.data.hash_tree_root(), epoch) .expect("aggregated signature proof verification failed"); } } @@ -166,12 +197,11 @@ fn test_produce_block_sequential_slots() { // Produce block for slot 1 let (block1_root, block1, _sig1) = - produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) - .expect("block1 should succeed"); + produce_block_with_signatures(&mut store, Slot(1), 1).expect("block1 should succeed"); // Verify first block is properly created assert_eq!(block1.slot, Slot(1)); - assert_eq!(block1.proposer_index, ValidatorIndex(1)); + assert_eq!(block1.proposer_index, 1); assert!(store.blocks.contains_key(&block1_root)); assert!(store.states.contains_key(&block1_root)); @@ -181,12 +211,11 @@ fn test_produce_block_sequential_slots() { // Produce block for slot 2 (will build on genesis due to forkchoice) let (block2_root, block2, _sig2) = - produce_block_with_signatures(&mut store, Slot(2), ValidatorIndex(2)) - .expect("block2 should succeed"); + produce_block_with_signatures(&mut store, Slot(2), 2).expect("block2 should succeed"); // Verify block properties assert_eq!(block2.slot, Slot(2)); - assert_eq!(block2.proposer_index, ValidatorIndex(2)); + assert_eq!(block2.proposer_index, 2); // The parent should be genesis (the current head), not block1 let genesis_hash = store.head; @@ -206,22 +235,21 @@ fn test_produce_block_empty_attestations() { store.latest_known_attestations.clear(); let slot = Slot(3); - let validator_idx = ValidatorIndex(3); + let validator_idx = 3; - let (_root, block, _sig) = - produce_block_with_signatures(&mut store, slot, validator_idx) - .expect("block production should succeed"); + let (_root, block, _sig) = produce_block_with_signatures(&mut store, slot, validator_idx) + .expect("block production should succeed"); // Should produce valid block with empty attestations assert_eq!(block.body.attestations.len_usize(), 0); assert_eq!(block.slot, slot); assert_eq!(block.proposer_index, validator_idx); - assert_ne!(block.state_root, Bytes32::default()); + assert_ne!(block.state_root, H256::default()); } #[test] fn test_produce_block_state_consistency() { - let mut store = create_test_store(); + let (mut store, keys) = create_test_store_with_signers(); // Add an attestation for validator 7 let head_block = store.blocks[&store.head].clone(); @@ -236,19 +264,21 @@ fn test_produce_block_state_consistency() { target, source: store.latest_justified.clone(), }; - store - .latest_known_attestations - .insert(ValidatorIndex(7), data.clone()); + store.latest_known_attestations.insert(7, data.clone()); let sig_key = SignatureKey { validator_id: 7, - data_root: Bytes32(data.hash_tree_root()), + data_root: data.hash_tree_root(), }; - store - .gossip_signatures - .insert(sig_key, make_mock_signature()); + store.gossip_signatures.insert( + sig_key, + keys.get(&7) + .unwrap() + .sign(data.hash_tree_root(), head_block.slot.0 as u32) + .unwrap(), + ); let slot = Slot(4); - let validator_idx = ValidatorIndex(4); + let validator_idx = 4; let (block_root, block, signatures) = produce_block_with_signatures(&mut store, slot, validator_idx) @@ -256,7 +286,7 @@ fn test_produce_block_state_consistency() { // Verify the stored state matches the block's state root let stored_state = &store.states[&block_root]; - assert_eq!(Bytes32(stored_state.hash_tree_root()), block.state_root); + assert_eq!(stored_state.hash_tree_root(), block.state_root); // Verify attestation count matches signature count. // We added 1 attestation (validator 7), so expect exactly 1. @@ -281,13 +311,13 @@ fn test_produce_block_state_consistency() { .get(vid) .expect("validator index out of range") .pubkey + .clone() }) .collect(); - let message: [u8; 32] = agg_att.data.data_root_bytes().0.into(); let epoch = agg_att.data.slot.0 as u32; proof .proof_data - .verify(&public_keys, &message, epoch) + .verify(public_keys, agg_att.data.hash_tree_root(), epoch) .expect("aggregated signature proof verification failed"); } } @@ -302,22 +332,21 @@ fn test_block_production_then_attestation() { // Proposer produces block for slot 1 let (_root, _block, _sig) = - produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) - .expect("block should succeed"); + produce_block_with_signatures(&mut store, Slot(1), 1).expect("block should succeed"); // Update store state after block production update_head(&mut store); // Other validator creates attestation for slot 2 - let attestor_idx = ValidatorIndex(7); + let attestor_idx = 7; let attestation_data = produce_attestation_data(&store, Slot(2)); let attestation = Attestation { - validator_id: Uint64(attestor_idx.0), + validator_id: attestor_idx, data: attestation_data, }; // Attestation should reference the new block as head (if it became head) - assert_eq!(attestation.validator_id, Uint64(attestor_idx.0)); + assert_eq!(attestation.validator_id, attestor_idx); assert_eq!(attestation.data.slot, Slot(2)); // The attestation should be consistent with current forkchoice state @@ -330,8 +359,7 @@ fn test_multiple_validators_coordination() { // Validator 1 produces block for slot 1 let (block1_root, block1, _sig1) = - produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) - .expect("block1 should succeed"); + produce_block_with_signatures(&mut store, Slot(1), 1).expect("block1 should succeed"); let block1_hash = block1_root; // Validators 2-5 create attestations for slot 2 @@ -340,7 +368,7 @@ fn test_multiple_validators_coordination() { for i in 2..6u64 { let data = produce_attestation_data(&store, Slot(2)); let attestation = Attestation { - validator_id: Uint64(i), + validator_id: i, data, }; attestations.push(attestation); @@ -358,12 +386,11 @@ fn test_multiple_validators_coordination() { // After processing block1, head should be block1 (fork choice walks the tree) // So block2 will build on block1 let (block2_root, block2, _sig2) = - produce_block_with_signatures(&mut store, Slot(2), ValidatorIndex(2)) - .expect("block2 should succeed"); + produce_block_with_signatures(&mut store, Slot(2), 2).expect("block2 should succeed"); // Verify block properties assert_eq!(block2.slot, Slot(2)); - assert_eq!(block2.proposer_index, ValidatorIndex(2)); + assert_eq!(block2.proposer_index, 2); // Both blocks should exist in the store assert!(store.blocks.contains_key(&block1_hash)); @@ -387,42 +414,37 @@ fn test_validator_edge_cases() { let mut store = create_test_store(); // Test with validator index equal to number of validators - 1 - let max_validator = ValidatorIndex(9); // Last validator (0-indexed, 10 total) + let max_validator = 9; // Last validator (0-indexed, 10 total) let slot = Slot(9); // This validator's slot // Should be able to produce block - let (_root, block, _sig) = - produce_block_with_signatures(&mut store, slot, max_validator) - .expect("max validator block should succeed"); + let (_root, block, _sig) = produce_block_with_signatures(&mut store, slot, max_validator) + .expect("max validator block should succeed"); assert_eq!(block.proposer_index, max_validator); // Should be able to produce attestation let attestation_data = produce_attestation_data(&store, Slot(10)); let attestation = Attestation { - validator_id: Uint64(max_validator.0), + validator_id: max_validator, data: attestation_data, }; - assert_eq!(attestation.validator_id, Uint64(max_validator.0)); + assert_eq!(attestation.validator_id, max_validator); } #[test] fn test_validator_operations_empty_store() { - use containers::block::BlockWithAttestation; - use containers::block::SignedBlockWithAttestation; - use fork_choice::store::get_forkchoice_store; - let config = Config { genesis_time: 1000 }; // Create validators list with 3 validators let validators = vec![Validator::default(); 3]; - let state = State::generate_genesis_with_validators(Uint64(1000), validators); + let state = State::generate_genesis_with_validators(1000, validators); let genesis_body = BlockBody::default(); let genesis = Block { slot: Slot(0), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32::default(), - state_root: Bytes32(state.hash_tree_root()), + proposer_index: 0, + parent_root: H256::default(), + state_root: state.hash_tree_root(), body: genesis_body, }; @@ -440,16 +462,15 @@ fn test_validator_operations_empty_store() { // Should be able to produce block and attestation let (_root, block, _sig) = - produce_block_with_signatures(&mut store, Slot(1), ValidatorIndex(1)) - .expect("block should succeed"); + produce_block_with_signatures(&mut store, Slot(1), 1).expect("block should succeed"); let attestation_data = produce_attestation_data(&store, Slot(1)); let attestation = Attestation { - validator_id: Uint64(2), + validator_id: 2, data: attestation_data, }; assert_eq!(block.slot, Slot(1)); - assert_eq!(attestation.validator_id, Uint64(2)); + assert_eq!(attestation.validator_id, 2); } // --------------------------------------------------------------------------- @@ -460,17 +481,17 @@ fn test_validator_operations_empty_store() { fn test_produce_block_wrong_proposer() { let mut store = create_test_store(); let slot = Slot(5); - let wrong_proposer = ValidatorIndex(3); // Should be validator 5 for slot 5 + let wrong_proposer = 3; // Should be validator 5 for slot 5 let result = produce_block_with_signatures(&mut store, slot, wrong_proposer); assert!(result.is_err()); - assert!(result.unwrap_err().contains("is not the proposer for slot")); + assert!(format!("{:?}", result.unwrap_err()).contains("is not the proposer for slot")); } #[test] fn test_produce_block_missing_parent_state() { let checkpoint = Checkpoint { - root: Bytes32(ssz::H256::from_slice(&[0xab; 32])), + root: H256::from_slice(&[0xab; 32]), slot: Slot(0), }; @@ -478,8 +499,8 @@ fn test_produce_block_missing_parent_state() { let store = Store { time: 100, config: Config { genesis_time: 1000 }, - head: Bytes32(ssz::H256::from_slice(&[0xab; 32])), - safe_target: Bytes32(ssz::H256::from_slice(&[0xab; 32])), + head: H256::from_slice(&[0xab; 32]), + safe_target: H256::from_slice(&[0xab; 32]), latest_justified: checkpoint.clone(), latest_finalized: checkpoint, blocks: Default::default(), @@ -490,7 +511,7 @@ fn test_produce_block_missing_parent_state() { // Missing head in get_proposal_head -> KeyError equivalent let result = std::panic::catch_unwind(|| { let mut s = store; - produce_block_with_signatures(&mut s, Slot(1), ValidatorIndex(1)) + produce_block_with_signatures(&mut s, Slot(1), 1) }); assert!(result.is_err()); } @@ -503,18 +524,18 @@ fn test_validator_operations_invalid_parameters() { let num_validators = state.validators.len_u64(); // Very large validator index (should work mathematically) - let large_validator = ValidatorIndex(1_000_000); + let large_validator = 1_000_000; let large_slot = Slot(1_000_000); // is_proposer_for should work (though likely return False) - let result = large_slot.0 % num_validators == large_validator.0; + let result = large_slot.0 % num_validators == large_validator; let _: bool = result; // Attestation can be created for any validator let attestation_data = produce_attestation_data(&store, Slot(1)); let attestation = Attestation { - validator_id: Uint64(large_validator.0), + validator_id: large_validator, data: attestation_data, }; - assert_eq!(attestation.validator_id, Uint64(large_validator.0)); + assert_eq!(attestation.validator_id, large_validator); } diff --git a/lean_client/fork_choice/tests/unit_tests/votes.rs b/lean_client/fork_choice/tests/unit_tests/votes.rs index 2b20fe2..6b40cab 100644 --- a/lean_client/fork_choice/tests/unit_tests/votes.rs +++ b/lean_client/fork_choice/tests/unit_tests/votes.rs @@ -4,24 +4,19 @@ //! using the devnet2 AttestationData structure per leanSpec. use super::common::create_test_store; -use containers::{ - attestation::AttestationData, - block::{Block, BlockBody}, - checkpoint::Checkpoint, - Bytes32, Slot, ValidatorIndex, -}; +use containers::{AttestationData, Block, BlockBody, Checkpoint, Slot}; use fork_choice::store::get_fork_choice_head; -use ssz::SszHash; +use ssz::{H256, SszHash}; use std::collections::HashMap; /// Helper to create an AttestationData for devnet2 (per leanSpec) fn create_attestation_data( slot: u64, - head_root: Bytes32, + head_root: H256, head_slot: u64, - target_root: Bytes32, + target_root: H256, target_slot: u64, - source_root: Bytes32, + source_root: H256, source_slot: u64, ) -> AttestationData { AttestationData { @@ -58,7 +53,7 @@ fn test_single_vote_updates_head() { ); let mut attestations = HashMap::new(); - attestations.insert(ValidatorIndex(0), attestation); + attestations.insert(0, attestation); let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); @@ -76,7 +71,7 @@ fn test_multiple_votes_same_block() { for i in 0..5 { let attestation = create_attestation_data(1, genesis_root, 0, genesis_root, 0, genesis_root, 0); - attestations.insert(ValidatorIndex(i), attestation); + attestations.insert(i, attestation); } let head = get_fork_choice_head(&store, genesis_root, &attestations, 0); @@ -93,16 +88,16 @@ fn test_competing_votes_different_blocks() { // Create two competing blocks at slot 1 let block_a = Block { slot: Slot(1), - proposer_index: ValidatorIndex(0), + proposer_index: 0, parent_root: genesis_root, - state_root: Bytes32::default(), + state_root: H256::default(), body: BlockBody::default(), }; - let block_a_root = Bytes32(block_a.hash_tree_root()); + let block_a_root = block_a.hash_tree_root(); let mut block_b = block_a.clone(); - block_b.proposer_index = ValidatorIndex(1); // Different proposer to get different root - let block_b_root = Bytes32(block_b.hash_tree_root()); + block_b.proposer_index = 1; // Different proposer to get different root + let block_b_root = block_b.hash_tree_root(); // Per leanSpec, store.blocks contains Block directly store.blocks.insert(block_a_root, block_a); @@ -112,13 +107,13 @@ fn test_competing_votes_different_blocks() { let mut attestations = HashMap::new(); for i in 0..3 { attestations.insert( - ValidatorIndex(i), + i, create_attestation_data(1, block_a_root, 1, genesis_root, 0, genesis_root, 0), ); } for i in 3..5 { attestations.insert( - ValidatorIndex(i), + i, create_attestation_data(1, block_b_root, 1, genesis_root, 0, genesis_root, 0), ); } @@ -137,21 +132,21 @@ fn test_vote_weight_accumulation() { // Create a chain: genesis -> block1 -> block2 let block1 = Block { slot: Slot(1), - proposer_index: ValidatorIndex(0), + proposer_index: 0, parent_root: genesis_root, - state_root: Bytes32::default(), + state_root: H256::default(), body: BlockBody::default(), }; - let block1_root = Bytes32(block1.hash_tree_root()); + let block1_root = block1.hash_tree_root(); let block2 = Block { slot: Slot(2), - proposer_index: ValidatorIndex(0), + proposer_index: 0, parent_root: block1_root, - state_root: Bytes32::default(), + state_root: H256::default(), body: BlockBody::default(), }; - let block2_root = Bytes32(block2.hash_tree_root()); + let block2_root = block2.hash_tree_root(); // Per leanSpec, store.blocks contains Block directly store.blocks.insert(block1_root, block1); @@ -160,7 +155,7 @@ fn test_vote_weight_accumulation() { // Vote for block2 - should accumulate to block1 as well let mut attestations = HashMap::new(); attestations.insert( - ValidatorIndex(0), + 0, create_attestation_data(2, block2_root, 2, genesis_root, 0, genesis_root, 0), ); @@ -180,13 +175,13 @@ fn test_duplicate_vote_uses_latest() { // Insert a vote attestations.insert( - ValidatorIndex(0), + 0, create_attestation_data(1, genesis_root, 0, genesis_root, 0, genesis_root, 0), ); // "Update" with same validator - only latest is kept attestations.insert( - ValidatorIndex(0), + 0, create_attestation_data(2, genesis_root, 0, genesis_root, 0, genesis_root, 0), ); @@ -201,12 +196,12 @@ fn test_duplicate_vote_uses_latest() { fn test_vote_for_unknown_block_ignored() { let store = create_test_store(); let genesis_root = store.head; - let unknown_root = Bytes32(ssz::H256::from_slice(&[0xff; 32])); + let unknown_root = H256::from_slice(&[0xff; 32]); // Vote for block that doesn't exist let mut attestations = HashMap::new(); attestations.insert( - ValidatorIndex(0), + 0, create_attestation_data(1, unknown_root, 1, genesis_root, 0, genesis_root, 0), ); diff --git a/lean_client/networking/Cargo.toml b/lean_client/networking/Cargo.toml index b4c2b89..d477a05 100644 --- a/lean_client/networking/Cargo.toml +++ b/lean_client/networking/Cargo.toml @@ -1,39 +1,34 @@ [package] name = "networking" -version = "0.1.0" -edition = "2024" - -[features] -default = [] +edition = { workspace = true } [dependencies] -env-config = { path = "../env-config", default-features = false } -containers = {workspace = true} -alloy-primitives = { workspace = true} -libp2p = {workspace = true} -snap = {workspace = true} -sha2 = { workspace = true } anyhow = { workspace = true } -async-trait = "0.1" -discv5 = "0.10.2" -enr = { version = "0.13", features = ["k256"] } -k256 = "0.13" -futures = "0.3" -libp2p-identity = { version = "0.2", features = ["secp256k1"] } -libp2p-mplex = "0.39" -parking_lot = "0.12" -rand = "0.8" -tokio = { workspace = true } -tracing = "0.1" -yamux = "0.12" -ssz = { workspace = true } +async-trait = { workspace = true } +containers = { workspace = true } +derive_more = { workspace = true } +discv5 = { workspace = true } +enr = { workspace = true } +env-config = { workspace = true } +futures = { workspace = true } +hex = { workspace = true } +k256 = { workspace = true } +libp2p = { workspace = true } +libp2p-identity = { workspace = true } +libp2p-mplex = { workspace = true } +parking_lot = { workspace = true } +rand = { workspace = true } serde = { workspace = true } serde_yaml = { workspace = true } -hex = "0.4.3" -tiny-keccak = "2.0.2" -derive_more = "2.1.1" +sha2 = { workspace = true } +snap = { workspace = true } +ssz = { workspace = true } +tiny-keccak = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } +yamux = { workspace = true } [dev-dependencies] -hex = "0.4" -num-bigint = "0.4" -num-traits = "0.2" +hex = { workspace = true } +num-bigint = { workspace = true } +num-traits = { workspace = true } diff --git a/lean_client/networking/src/gossipsub/message.rs b/lean_client/networking/src/gossipsub/message.rs index 4ac1ae1..6e770f4 100644 --- a/lean_client/networking/src/gossipsub/message.rs +++ b/lean_client/networking/src/gossipsub/message.rs @@ -2,8 +2,8 @@ use crate::gossipsub::topic::GossipsubKind; use crate::gossipsub::topic::GossipsubTopic; use containers::SignedAttestation; use containers::SignedBlockWithAttestation; -use containers::ssz::SszReadDefault; use libp2p::gossipsub::TopicHash; +use ssz::SszReadDefault as _; pub enum GossipsubMessage { Block(SignedBlockWithAttestation), diff --git a/lean_client/networking/src/network/service.rs b/lean_client/networking/src/network/service.rs index b0d37ee..3dd6624 100644 --- a/lean_client/networking/src/network/service.rs +++ b/lean_client/networking/src/network/service.rs @@ -8,7 +8,6 @@ use std::{ }; use anyhow::{Result, anyhow}; -use containers::ssz::SszWrite; use derive_more::Display; use discv5::Enr; use futures::StreamExt; @@ -22,7 +21,9 @@ use libp2p::{ }; use libp2p_identity::{Keypair, PeerId}; use parking_lot::Mutex; +use rand::seq::IndexedRandom; use serde::{Deserialize, Serialize}; +use ssz::{H256, SszWrite as _}; use tokio::select; use tokio::time::{Duration, MissedTickBehavior, interval}; use tracing::{debug, info, trace, warn}; @@ -643,8 +644,7 @@ where if peers.is_empty() { None } else { - use rand::seq::SliceRandom; - peers.choose(&mut rand::thread_rng()).copied() + peers.choose(&mut rand::rng()).copied() } } @@ -742,11 +742,7 @@ where .send_request(&peer_id, request); } - pub fn send_blocks_by_root_request( - &mut self, - peer_id: PeerId, - roots: Vec, - ) { + pub fn send_blocks_by_root_request(&mut self, peer_id: PeerId, roots: Vec) { if roots.is_empty() { return; } diff --git a/lean_client/networking/src/req_resp.rs b/lean_client/networking/src/req_resp.rs index 592c746..52a931f 100644 --- a/lean_client/networking/src/req_resp.rs +++ b/lean_client/networking/src/req_resp.rs @@ -2,14 +2,14 @@ use std::io; use std::io::{Read, Write}; use async_trait::async_trait; -use containers::ssz::{SszReadDefault, SszWrite}; -use containers::{Bytes32, SignedBlockWithAttestation, Status}; +use containers::{SignedBlockWithAttestation, Status}; use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use libp2p::request_response::{ Behaviour as RequestResponse, Codec, Config, Event, ProtocolSupport, }; use snap::read::FrameDecoder; use snap::write::FrameEncoder; +use ssz::{H256, SszReadDefault as _, SszWrite as _}; pub const MAX_REQUEST_BLOCKS: usize = 1024; @@ -28,10 +28,10 @@ impl AsRef for LeanProtocol { #[derive(Debug, Clone, PartialEq, Eq)] pub enum LeanRequest { Status(Status), - BlocksByRoot(Vec), + BlocksByRoot(Vec), } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub enum LeanResponse { Status(Status), BlocksByRoot(Vec), @@ -67,7 +67,7 @@ impl LeanCodec { LeanRequest::BlocksByRoot(roots) => { let mut bytes = Vec::new(); for root in roots { - bytes.extend_from_slice(root.0.as_bytes()); + bytes.extend_from_slice(root.as_bytes()); } bytes } @@ -96,7 +96,7 @@ impl LeanCodec { if chunk.len() == 32 { let mut root = [0u8; 32]; root.copy_from_slice(chunk); - roots.push(Bytes32(containers::ssz::H256::from(root))); + roots.push(H256::from(root)); } } if roots.len() > MAX_REQUEST_BLOCKS { diff --git a/lean_client/networking/src/types.rs b/lean_client/networking/src/types.rs index 4e7bde8..5706365 100644 --- a/lean_client/networking/src/types.rs +++ b/lean_client/networking/src/types.rs @@ -2,8 +2,9 @@ use std::{collections::HashMap, fmt::Display}; use anyhow::{Result, anyhow}; use async_trait::async_trait; -use containers::{Bytes32, SignedAttestation, SignedBlockWithAttestation}; +use containers::{SignedAttestation, SignedBlockWithAttestation}; use serde::{Deserialize, Serialize}; +use ssz::H256; use tokio::sync::mpsc; use crate::serde_utils::quoted_u64; @@ -107,7 +108,7 @@ impl PeerCount { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub enum ChainMessage { ProcessBlock { signed_block_with_attestation: SignedBlockWithAttestation, @@ -167,11 +168,11 @@ impl Display for ChainMessage { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub enum OutboundP2pRequest { GossipBlockWithAttestation(SignedBlockWithAttestation), GossipAttestation(SignedAttestation), - RequestBlocksByRoot(Vec), + RequestBlocksByRoot(Vec), } #[async_trait] diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index 97a9d75..02b55c4 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -1,37 +1,32 @@ use clap::Parser; -use containers::block::BlockSignatures; -use containers::ssz::{PersistentList, SszHash}; use containers::{ - attestation::{Attestation, AttestationData}, - block::{Block, BlockBody, BlockWithAttestation, SignedBlockWithAttestation}, - checkpoint::Checkpoint, - config::Config, - ssz, - state::State, - types::{Bytes32, Uint64, ValidatorIndex}, - Signature, Slot, + Attestation, AttestationData, Block, BlockBody, BlockSignatures, BlockWithAttestation, + Checkpoint, Config, SignedBlockWithAttestation, Slot, State, Validator, }; +use ethereum_types::H256; use fork_choice::{ handlers::{on_attestation, on_block, on_tick}, - store::{get_forkchoice_store, Store, INTERVALS_PER_SLOT}, + store::{INTERVALS_PER_SLOT, Store, get_forkchoice_store}, }; use libp2p_identity::Keypair; use networking::gossipsub::config::GossipsubConfig; use networking::gossipsub::topic::get_topics; use networking::network::{NetworkService, NetworkServiceConfig}; use networking::types::{ChainMessage, OutboundP2pRequest}; +use ssz::{PersistentList, SszHash}; use std::net::IpAddr; -use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; use tokio::{ sync::mpsc, task, - time::{interval, Duration}, + time::{Duration, interval}, }; use tracing::level_filters::LevelFilter; use tracing::{debug, info, warn}; use validator::{ValidatorConfig, ValidatorService}; +use xmss::{PublicKey, Signature}; fn load_node_key(path: &str) -> Result> { let hex_str = std::fs::read_to_string(path)?.trim().to_string(); @@ -60,11 +55,7 @@ fn print_chain_status(store: &Store, connected_peers: u64) { let state_root = block.state_root; (head_root, parent_root, state_root) } else { - ( - Bytes32(ssz::H256::zero()), - Bytes32(ssz::H256::zero()), - Bytes32(ssz::H256::zero()), - ) + (H256::zero(), H256::zero(), H256::zero()) }; // Read from store's checkpoints (updated by on_block, reflects highest seen) @@ -81,9 +72,9 @@ fn print_chain_status(store: &Store, connected_peers: u64) { println!("+---------------------------------------------------------------+"); println!(" Connected Peers: {}", connected_peers); println!("+---------------------------------------------------------------+"); - println!(" Head Block Root: 0x{:x}", head_root.0); - println!(" Parent Block Root: 0x{:x}", parent_root.0); - println!(" State Root: 0x{:x}", state_root.0); + println!(" Head Block Root: 0x{:x}", head_root); + println!(" Parent Block Root: 0x{:x}", parent_root); + println!(" State Root: 0x{:x}", state_root); println!( " Timely: {}", if timely { "YES" } else { "NO" } @@ -91,11 +82,11 @@ fn print_chain_status(store: &Store, connected_peers: u64) { println!("+---------------------------------------------------------------+"); println!( " Latest Justified: Slot {:>5} | Root: 0x{:x}", - justified.slot.0, justified.root.0 + justified.slot.0, justified.root ); println!( " Latest Finalized: Slot {:>5} | Root: 0x{:x}", - finalized.slot.0, finalized.root.0 + finalized.slot.0, finalized.root ); println!("+===============================================================+\n"); } @@ -157,16 +148,15 @@ async fn main() { let genesis_config = containers::GenesisConfig::load_from_file(genesis_path) .expect("Failed to load genesis config"); - let validators: Vec = genesis_config + let validators: Vec = genesis_config .genesis_validators .iter() .enumerate() .map(|(i, v_str)| { - let pubkey = containers::public_key::PublicKey::from_hex(v_str) - .expect("Invalid genesis validator pubkey"); - containers::validator::Validator { + let pubkey: PublicKey = v_str.parse().expect("Invalid genesis validator pubkey"); + Validator { pubkey, - index: Uint64(i as u64), + index: i as u64, } }) .collect(); @@ -175,40 +165,40 @@ async fn main() { } else { let num_validators = 3; let validators = (0..num_validators) - .map(|i| containers::validator::Validator { - pubkey: containers::public_key::PublicKey::default(), - index: Uint64(i as u64), + .map(|i| Validator { + pubkey: PublicKey::default(), + index: i as u64, }) .collect(); (1763757427, validators) }; - let genesis_state = State::generate_genesis_with_validators(Uint64(genesis_time), validators); + let genesis_state = State::generate_genesis_with_validators(genesis_time, validators); let genesis_block = Block { slot: Slot(0), - proposer_index: ValidatorIndex(0), - parent_root: Bytes32(ssz::H256::zero()), - state_root: Bytes32(genesis_state.hash_tree_root()), + proposer_index: 0, + parent_root: H256::zero(), + state_root: genesis_state.hash_tree_root(), body: BlockBody { attestations: Default::default(), }, }; let genesis_proposer_attestation = Attestation { - validator_id: Uint64(0), + validator_id: 0, data: AttestationData { slot: Slot(0), head: Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), }, target: Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), }, source: Checkpoint { - root: Bytes32(ssz::H256::zero()), + root: H256::zero(), slot: Slot(0), }, }, @@ -388,16 +378,16 @@ async fn main() { if let Some(proposer_idx) = vs.get_proposer_for_slot(Slot(current_slot)) { info!( slot = current_slot, - proposer = proposer_idx.0, + proposer = proposer_idx, "Our turn to propose block!" ); match vs.build_block_proposal(&mut store, Slot(current_slot), proposer_idx) { Ok(signed_block) => { - let block_root = containers::block::compute_block_root(&signed_block.message.block); + let block_root = signed_block.message.block.hash_tree_root(); info!( slot = current_slot, - block_root = %format!("0x{:x}", block_root.0), + block_root = %format!("0x{:x}", block_root), "Built block, processing and gossiping" ); @@ -480,14 +470,14 @@ async fn main() { should_gossip, .. } => { - let block_slot = signed_block_with_attestation.message.block.slot.0; - let proposer = signed_block_with_attestation.message.block.proposer_index.0; - let block_root = containers::block::compute_block_root(&signed_block_with_attestation.message.block); + let block_slot = signed_block_with_attestation.message.block.slot; + let proposer = signed_block_with_attestation.message.block.proposer_index; + let block_root = signed_block_with_attestation.message.block.hash_tree_root(); let parent_root = signed_block_with_attestation.message.block.parent_root; info!( - slot = block_slot, - block_root = %format!("0x{:x}", block_root.0), + slot = block_slot.0, + block_root = %format!("0x{:x}", block_root), "Processing block built by Validator {}", proposer ); @@ -509,21 +499,21 @@ async fn main() { ) { warn!("Failed to gossip block: {}", e); } else { - info!(slot = block_slot, "Broadcasted block"); + info!(slot = block_slot.0, "Broadcasted block"); } } } - Err(e) if e.starts_with("Err: (Fork-choice::Handlers::OnBlock) Block queued") => { + Err(e) if format!("{e:?}").starts_with("Err: (Fork-choice::Handlers::OnBlock) Block queued") => { debug!("Block queued, requesting missing parent: {}", e); // Request missing parent block from peers - if !parent_root.0.is_zero() { + if !parent_root.is_zero() { if let Err(req_err) = outbound_p2p_sender.send( OutboundP2pRequest::RequestBlocksByRoot(vec![parent_root]) ) { warn!("Failed to request missing parent block: {}", req_err); } else { - debug!("Requested missing parent block: 0x{:x}", parent_root.0); + debug!("Requested missing parent block: 0x{:x}", parent_root); } } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json index 22dba6e..2fb29fc 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_accumulation_full_validator_set.json @@ -287,7 +287,7 @@ "hash": "0xdbbeaea6e8f9a310bf70b5d73ca417b937d913619861e17b159518e04f574985", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_accumulation_full_validator_set[fork_Devnet]", - "description": "All validators contribute attestations across both dictionaries.\n\n Scenario\n --------\n Process blocks at slots 1, 2, 3, 4 (complete validator rotation).\n\n Expected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - After slot 3: new attestations = 1, known attestations = 2\n - After slot 4: new attestations = 1, known attestations = 3 (total: 4 validators)\n\n Why This Matters\n ----------------\n With 4 validators and consecutive blocks, each validator proposes once.\n\n Attestations accumulate across both dictionaries:\n - new: current slot's proposer\n - known: all previous proposers\n\n The total (new + known) equals the number of unique validators who proposed.", + "description": "All validators contribute attestations across both dictionaries.\n\nScenario\n--------\nProcess blocks at slots 1, 2, 3, 4 (complete validator rotation).\n\nExpected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - After slot 3: new attestations = 1, known attestations = 2\n - After slot 4: new attestations = 1, known attestations = 3 (total: 4 validators)\n\nWhy This Matters\n----------------\nWith 4 validators and consecutive blocks, each validator proposes once.\n\nAttestations accumulate across both dictionaries:\n- new: current slot's proposer\n- known: all previous proposers\n\nThe total (new + known) equals the number of unique validators who proposed.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json index 318541a..753b244 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestation_superseding_same_validator.json @@ -168,7 +168,7 @@ "hash": "0x5fb3cc07e42126611049361cd37c1dddf1a6a1a7b0a53fb76e5acd6217e60478", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestation_superseding_same_validator[fork_Devnet]", - "description": "Newer attestation from same validator supersedes older attestation.\n\n Scenario\n --------\n Process blocks at slots 1 and 5 (same proposer: validator 1).\n\n Expected:\n - After slot 1: validator 1 attests to slot 1\n - After slot 5: validator 1 attests to slot 5 (supersedes slot 1)\n\n Why This Matters\n ----------------\n With round-robin proposer selection, slots 1 and 5 use the same validator.\n\n When that validator proposes again, their newer attestation supersedes the older one.\n Both dictionaries are keyed by validator index, so only the most recent\n attestation per validator is retained.\n\n Key insight: Attestations accumulate across validators but supersede within validators.", + "description": "Newer attestation from same validator supersedes older attestation.\n\nScenario\n--------\nProcess blocks at slots 1 and 5 (same proposer: validator 1).\n\nExpected:\n - After slot 1: validator 1 attests to slot 1\n - After slot 5: validator 1 attests to slot 5 (supersedes slot 1)\n\nWhy This Matters\n----------------\nWith round-robin proposer selection, slots 1 and 5 use the same validator.\n\nWhen that validator proposes again, their newer attestation supersedes the older one.\nBoth dictionaries are keyed by validator index, so only the most recent\nattestation per validator is retained.\n\nKey insight: Attestations accumulate across validators but supersede within validators.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json index a4638e6..7bc848a 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_attestations_move_to_known_between_blocks.json @@ -177,7 +177,7 @@ "hash": "0xab273bd905d90599241dc8c0c4f4f2a215e7a2a9c7841f4614e8e42dbe5f7890", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_attestations_move_to_known_between_blocks[fork_Devnet]", - "description": "Attestations move from latest_new to latest_known between blocks.\n\n Scenario\n --------\n Process blocks at slots 1 and 2 (different proposers: validators 1 and 2).\n\n Expected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - Validator 1's attestation moved to known with correct checkpoints\n - Validator 2's attestation in new with correct checkpoints\n\n Why This Matters\n ----------------\n The interval tick system drives attestation migration between slots.\n\n Before processing the next block, interval ticks move all attestations from\n new \u2192 known and clear the new dictionary. Then the next block's proposer\n attestation enters the now-empty new dictionary.\n\n This creates the attestation pipeline:\n - Enter via new (arrivals)\n - Graduate to known (accepted for fork choice)", + "description": "Attestations move from latest_new to latest_known between blocks.\n\nScenario\n--------\nProcess blocks at slots 1 and 2 (different proposers: validators 1 and 2).\n\nExpected:\n - After slot 1: new attestations = 1, known attestations = 0\n - After slot 2: new attestations = 1, known attestations = 1\n - Validator 1's attestation moved to known with correct checkpoints\n - Validator 2's attestation in new with correct checkpoints\n\nWhy This Matters\n----------------\nThe interval tick system drives attestation migration between slots.\n\nBefore processing the next block, interval ticks move all attestations from\nnew \u2192 known and clear the new dictionary. Then the next block's proposer\nattestation enters the now-empty new dictionary.\n\nThis creates the attestation pipeline:\n- Enter via new (arrivals)\n- Graduate to known (accepted for fork choice)", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json index 83d02a6..dab0680 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_extended_chain_attestation_superseding_pattern.json @@ -523,7 +523,7 @@ "hash": "0xcbdd6766ce8841f678f2e13cd980c3163cb78456053753bc6684cfd3c6a70c83", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_extended_chain_attestation_superseding_pattern[fork_Devnet]", - "description": "Attestation superseding pattern over two complete validator rotations.\n\n Scenario\n --------\n Process blocks at slots 1-8 (two complete validator rotations).\n\n Phase 1 (slots 1-4): Accumulation\n Validators each propose once, attestations accumulate to 4 total.\n\n Phase 2 (slots 5-8): Steady State\n Validators propose again, newer attestations supersede older ones.\n Total stays at 4, composition changes.\n\n Expected:\n - After slot 4: All 4 validators have attestations (v0 in new, v1-v3 in known)\n - After slot 5: Validator 1 supersedes their slot 1 attestation\n - After slot 8: All validators have their latest attestations from slots 5-8\n\n Why This Matters\n ----------------\n The system reaches steady state: one attestation per validator.\n\n As each validator proposes again, their new attestation supersedes their old one.\n The count remains constant (4), but the composition updates.\n\n This confirms superseding maintains correct state over time with no attestation\n leaks or unbounded growth.", + "description": "Attestation superseding pattern over two complete validator rotations.\n\nScenario\n--------\nProcess blocks at slots 1-8 (two complete validator rotations).\n\nPhase 1 (slots 1-4): Accumulation\n Validators each propose once, attestations accumulate to 4 total.\n\nPhase 2 (slots 5-8): Steady State\n Validators propose again, newer attestations supersede older ones.\n Total stays at 4, composition changes.\n\nExpected:\n - After slot 4: All 4 validators have attestations (v0 in new, v1-v3 in known)\n - After slot 5: Validator 1 supersedes their slot 1 attestation\n - After slot 8: All validators have their latest attestations from slots 5-8\n\nWhy This Matters\n----------------\nThe system reaches steady state: one attestation per validator.\n\nAs each validator proposes again, their new attestation supersedes their old one.\nThe count remains constant (4), but the composition updates.\n\nThis confirms superseding maintains correct state over time with no attestation\nleaks or unbounded growth.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json index 9224e5d..ac23bd6 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_proposer_attestation_appears_in_latest_new.json @@ -121,7 +121,7 @@ "hash": "0x0cfd631610f67a5a5e80a51d3407ca9429e0ffeee888cbf761b379482cc9ef76", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_proposer_attestation_appears_in_latest_new[fork_Devnet]", - "description": "Proposer attestation appears in latest_new after block processing.\n\n Scenario\n --------\n Process one block at slot 1 (proposer: validator 1).\n\n Expected:\n - validator 1's attestation has correct slot and checkpoint slots\n\n Why This Matters\n ----------------\n New proposer attestations enter the pipeline through `latest_new_attestations`,\n not directly into `latest_known_attestations`.\n\n This baseline test verifies the entry point of the attestation pipeline.\n All new attestations must enter through the \"new\" stage before graduating to \"known\".", + "description": "Proposer attestation appears in latest_new after block processing.\n\nScenario\n--------\nProcess one block at slot 1 (proposer: validator 1).\n\nExpected:\n - validator 1's attestation has correct slot and checkpoint slots\n\nWhy This Matters\n----------------\nNew proposer attestations enter the pipeline through `latest_new_attestations`,\nnot directly into `latest_known_attestations`.\n\nThis baseline test verifies the entry point of the attestation pipeline.\nAll new attestations must enter through the \"new\" stage before graduating to \"known\".", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json index 5180a4c..a9788b3 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_processing/test_slot_gaps_with_attestation_superseding.json @@ -272,7 +272,7 @@ "hash": "0x9b83a8ab2fb554ae0ec61bde795000e3a8157ac64960caaebda77db0e6c22abb", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_processing.py::test_slot_gaps_with_attestation_superseding[fork_Devnet]", - "description": "Attestation superseding works correctly with missed slots.\n\n Scenario\n --------\n Process blocks at slots 1, 3, 5, 7 (skipping even slots).\n Proposers: validators 1, 3, 1, 3 (same validators repeat).\n\n Expected:\n - After slot 1: Validator 1 attests\n - After slot 3: Validator 3 attests, validator 1 moved to known\n - After slot 5: Validator 1 attests again (supersedes old), validator 3 in known\n - After slot 7: Validator 3 attests again (supersedes old), validator 1 in known\n\n Why This Matters\n ----------------\n Missed slots are normal when proposers fail to produce blocks.\n\n With non-contiguous slots, round-robin means validators propose multiple times.\n When they do, their newer attestations supersede their older ones.\n\n Total count stays at 2 (unique validators) throughout slots 5-7.\n\n This confirms attestation processing and superseding work correctly with slot gaps\n across both dictionaries.", + "description": "Attestation superseding works correctly with missed slots.\n\nScenario\n--------\nProcess blocks at slots 1, 3, 5, 7 (skipping even slots).\nProposers: validators 1, 3, 1, 3 (same validators repeat).\n\nExpected:\n - After slot 1: Validator 1 attests\n - After slot 3: Validator 3 attests, validator 1 moved to known\n - After slot 5: Validator 1 attests again (supersedes old), validator 3 in known\n - After slot 7: Validator 3 attests again (supersedes old), validator 1 in known\n\nWhy This Matters\n----------------\nMissed slots are normal when proposers fail to produce blocks.\n\nWith non-contiguous slots, round-robin means validators propose multiple times.\nWhen they do, their newer attestations supersede their older ones.\n\nTotal count stays at 2 (unique validators) throughout slots 5-7.\n\nThis confirms attestation processing and superseding work correctly with slot gaps\nacross both dictionaries.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json index b0330ae..3d9ae73 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_advances_with_attestations.json @@ -268,7 +268,7 @@ "hash": "0xc067f96d03a7a7954424ae0c3f84c5660206cd637bcf43c1063de04e6297f131", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_advances_with_attestations[fork_Devnet]", - "description": "Attestation target advances as attestation weight accumulates.\n\n Scenario\n --------\n Build a longer chain (slots 1-5) where attestations cause target advancement.\n\n Expected:\n - Initial blocks: target stays at genesis (slot 0)\n - Later blocks: target advances as attestations accumulate\n - Target remains behind head for safety\n\n Why This Matters\n ----------------\n As validators attest to blocks, the safe target advances, which in turn\n allows the attestation target to move forward.\n\n This demonstrates the dynamic nature of target selection: conservative initially,\n but advancing as consensus strengthens through attestation accumulation.\n\n The target advances only when sufficient attestation weight supports it.", + "description": "Attestation target advances as attestation weight accumulates.\n\nScenario\n--------\nBuild a longer chain (slots 1-5) where attestations cause target advancement.\n\nExpected:\n - Initial blocks: target stays at genesis (slot 0)\n - Later blocks: target advances as attestations accumulate\n - Target remains behind head for safety\n\nWhy This Matters\n----------------\nAs validators attest to blocks, the safe target advances, which in turn\nallows the attestation target to move forward.\n\nThis demonstrates the dynamic nature of target selection: conservative initially,\nbut advancing as consensus strengthens through attestation accumulation.\n\nThe target advances only when sufficient attestation weight supports it.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json index b631916..9beee4e 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_at_genesis_initially.json @@ -151,7 +151,7 @@ "hash": "0x7ed6ed6cb6f816bdff8fb63c80379d445bdcf1d2b4812bb98cdfce907eb3e152", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_at_genesis_initially[fork_Devnet]", - "description": "Attestation target starts at genesis before safe target updates.\n\n Scenario\n --------\n Process two blocks at slots 1 and 2.\n\n Expected:\n - After slot 1: target = slot 0 (genesis/finalized)\n - After slot 2: target = slot 0 (genesis/finalized)\n - Target root automatically validated against block at slot 0\n\n Why This Matters\n ----------------\n Initially, the safe target is at genesis (slot 0), so the attestation\n target walks back from head to genesis.\n\n This conservative behavior ensures validators don't attest too far ahead\n before there's sufficient attestation weight to advance the safe target.", + "description": "Attestation target starts at genesis before safe target updates.\n\nScenario\n--------\nProcess two blocks at slots 1 and 2.\n\nExpected:\n - After slot 1: target = slot 0 (genesis/finalized)\n - After slot 2: target = slot 0 (genesis/finalized)\n - Target root automatically validated against block at slot 0\n\nWhy This Matters\n----------------\nInitially, the safe target is at genesis (slot 0), so the attestation\ntarget walks back from head to genesis.\n\nThis conservative behavior ensures validators don't attest too far ahead\nbefore there's sufficient attestation weight to advance the safe target.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json index 7eb85ce..5a7aad5 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_justifiable_constraint.json @@ -1243,7 +1243,7 @@ "hash": "0x7c8c29c2077f7e8c15f316b2e9f1732ad2a47b0f511e986f965cddbd7ee9bfb8", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_justifiable_constraint[fork_Devnet]", - "description": "Attestation target advances while respecting justifiability rules.\n\n Scenario\n --------\n Build a 10-slot chain and observe how the attestation target advances\n over time while remaining justifiable relative to genesis (finalized at slot 0).\n\n Justifiability Rules (see Slot.is_justifiable_after)\n -----------------------------------------------------\n\n The target starts from current head and looks back at most 3 slots towards safe target.\n\n Then, a slot is deemed justifiable at distance delta from finalization if:\n 1. delta \u2264 5\n 2. delta is a perfect square (1, 4, 9, 16, 25, ...)\n 3. delta is a pronic number (2, 6, 12, 20, 30, ...)\n\n Why This Matters\n ----------------\n The justifiability rules prevent long-range attacks by restricting which\n checkpoints validators can attest to. The mathematical pattern (perfect squares\n and pronic numbers) creates increasingly sparse justifiable slots as the chain\n grows beyond finalization, providing security guarantees.\n\n The test verifies that the target selection algorithm respects these rules\n and never selects a non-justifiable target.", + "description": "Attestation target advances while respecting justifiability rules.\n\nScenario\n--------\nBuild a 10-slot chain and observe how the attestation target advances\nover time while remaining justifiable relative to genesis (finalized at slot 0).\n\nJustifiability Rules (see Slot.is_justifiable_after)\n-----------------------------------------------------\n\nThe target starts from current head and looks back at most 3 slots towards safe target.\n\nThen, a slot is deemed justifiable at distance delta from finalization if:\n1. delta \u2264 5\n2. delta is a perfect square (1, 4, 9, 16, 25, ...)\n3. delta is a pronic number (2, 6, 12, 20, 30, ...)\n\nWhy This Matters\n----------------\nThe justifiability rules prevent long-range attacks by restricting which\ncheckpoints validators can attest to. The mathematical pattern (perfect squares\nand pronic numbers) creates increasingly sparse justifiable slots as the chain\ngrows beyond finalization, providing security guarantees.\n\nThe test verifies that the target selection algorithm respects these rules\nand never selects a non-justifiable target.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json index 2f2db34..0b25f02 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_extended_chain.json @@ -385,7 +385,7 @@ "hash": "0xccc60d324f44c015f7d3d4585c42c7563b4515017776acd163278d22a8e8b5e1", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_extended_chain[fork_Devnet]", - "description": "Attestation target advances progressively over extended chain.\n\n Scenario\n --------\n Build a longer chain (slots 1-8) observing target advancement pattern.\n\n Expected:\n - Initial slots: target at genesis (conservative)\n - Middle slots: target advances to slot 1\n - Target advances gradually, not jumping to head\n\n Why This Matters\n ----------------\n Over extended chains, the target selection should show smooth,\n gradual advancement as attestation weight accumulates.\n\n The target lags behind the head, providing a stable reference point that\n advances only when sufficient consensus has formed. This prevents validators\n from attesting too far ahead without adequate safety guarantees.", + "description": "Attestation target advances progressively over extended chain.\n\nScenario\n--------\nBuild a longer chain (slots 1-8) observing target advancement pattern.\n\nExpected:\n - Initial slots: target at genesis (conservative)\n - Middle slots: target advances to slot 1\n - Target advances gradually, not jumping to head\n\nWhy This Matters\n----------------\nOver extended chains, the target selection should show smooth,\ngradual advancement as attestation weight accumulates.\n\nThe target lags behind the head, providing a stable reference point that\nadvances only when sufficient consensus has formed. This prevents validators\nfrom attesting too far ahead without adequate safety guarantees.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json index 5d190f1..643ecae 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_attestation_target_selection/test_attestation_target_with_slot_gaps.json @@ -190,7 +190,7 @@ "hash": "0x3c5e09193b89f78bfbe3f1829a0e67edef275c0c44d2514da6e5400bd3c5b958", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_attestation_target_selection.py::test_attestation_target_with_slot_gaps[fork_Devnet]", - "description": "Attestation target handles missed slots correctly.\n\n Scenario\n --------\n Process blocks at slots 1, 3, 5 (skipping even slots).\n\n Expected:\n - Targets advance despite gaps\n - Targets remain justifiable\n - Safe target stays valid\n\n Why This Matters\n ----------------\n Missed slots are common when proposers fail or network partitions occur.\n\n The target selection must handle sparse block production gracefully,\n ensuring validators can still make progress even with gaps in the chain.", + "description": "Attestation target handles missed slots correctly.\n\nScenario\n--------\nProcess blocks at slots 1, 3, 5 (skipping even slots).\n\nExpected:\n - Targets advance despite gaps\n - Targets remain justifiable\n - Safe target stays valid\n\nWhy This Matters\n----------------\nMissed slots are common when proposers fail or network partitions occur.\n\nThe target selection must handle sparse block production gracefully,\nensuring validators can still make progress even with gaps in the chain.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json index 5d27ba7..d5678b3 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_advances_through_deep_chain.json @@ -836,7 +836,7 @@ "hash": "0xbfc7c8a714f454ddfcf28aaf49e16c9e4b9f90f0de78f667f11826a9cd4e567f", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_advances_through_deep_chain[fork_Devnet]", - "description": "Fork choice head advances through a deep chain correctly.\n\n Scenario\n --------\n Build a long chain (slots 1-20) and verify head reaches the end.\n\n Expected Behavior:\n - Head advances through all 20 blocks\n - Final head = slot 20\n - Fork choice scales to longer chains\n\n Why This Matters\n ----------------\n This tests that the fork choice algorithm scales to longer chains and\n correctly handles the tree-walking logic through many blocks.\n\n Real networks have chains thousands of blocks long. The algorithm must:\n - Efficiently traverse deep trees\n - Maintain correct head even with many ancestors\n - Not degrade in performance or correctness with depth\n\n A 20-block chain is a modest test of this scalability.", + "description": "Fork choice head advances through a deep chain correctly.\n\nScenario\n--------\nBuild a long chain (slots 1-20) and verify head reaches the end.\n\nExpected Behavior:\n - Head advances through all 20 blocks\n - Final head = slot 20\n - Fork choice scales to longer chains\n\nWhy This Matters\n----------------\nThis tests that the fork choice algorithm scales to longer chains and\ncorrectly handles the tree-walking logic through many blocks.\n\nReal networks have chains thousands of blocks long. The algorithm must:\n- Efficiently traverse deep trees\n- Maintain correct head even with many ancestors\n- Not degrade in performance or correctness with depth\n\nA 20-block chain is a modest test of this scalability.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json index cf03053..a009d28 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_switches_to_heavier_fork.json @@ -237,7 +237,7 @@ "hash": "0xaaf4d3533d5d07ff419462a6ec9acd98edd8e3cf1e26f3505489fc52d6d5467f", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_switches_to_heavier_fork[fork_Devnet]", - "description": "Fork choice head switches when a competing fork becomes heavier.\n\n Scenario\n --------\n Create two forks at slot 2, then extend one fork to make it heavier.\n\n Expected Behavior:\n - After fork A (slot 2): head = fork A\n - After fork B (slot 2): head = still fork A (tie-breaker)\n - After extending fork B (slot 3): head = slot 3 (fork B wins!)\n\n Why This Matters\n ----------------\n This demonstrates the core LMD-GHOST property: the head follows the heaviest\n subtree. When fork B is extended with a child block, that child's proposer\n implicitly attests to fork B, giving it more weight.\n\n Fork choice recognizes this weight increase and switches the head to fork B's\n descendant. This is how the protocol reaches consensus - validators converge\n on the fork with the most support (weight).\n\n This is also how reorgs happen: a previously non-canonical fork can become\n canonical if it gains more attestation weight.", + "description": "Fork choice head switches when a competing fork becomes heavier.\n\nScenario\n--------\nCreate two forks at slot 2, then extend one fork to make it heavier.\n\nExpected Behavior:\n - After fork A (slot 2): head = fork A\n - After fork B (slot 2): head = still fork A (tie-breaker)\n - After extending fork B (slot 3): head = slot 3 (fork B wins!)\n\nWhy This Matters\n----------------\nThis demonstrates the core LMD-GHOST property: the head follows the heaviest\nsubtree. When fork B is extended with a child block, that child's proposer\nimplicitly attests to fork B, giving it more weight.\n\nFork choice recognizes this weight increase and switches the head to fork B's\ndescendant. This is how the protocol reaches consensus - validators converge\non the fork with the most support (weight).\n\nThis is also how reorgs happen: a previously non-canonical fork can become\ncanonical if it gains more attestation weight.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json index 5cbfad8..507e0cd 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_deep_fork_split.json @@ -401,7 +401,7 @@ "hash": "0x04cab0a7de2cb20b31ad58fe67088cfd813fbd79164b2ff538bd8bfeb23c199b", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_deep_fork_split[fork_Devnet]", - "description": "Fork choice handles deep fork splits correctly.\n\n Scenario\n --------\n Create two forks that diverge at slot 2 and extend to different depths.\n\n Expected Behavior:\n - Fork A extends to slot 4\n - Fork B extends to slot 5\n - Head follows the longer (heavier) fork B\n\n Why This Matters\n ----------------\n In practice, forks can persist for multiple slots before one gains dominance.\n This tests that fork choice correctly follows the deeper fork, which has\n accumulated more proposer attestations along its chain.\n\n Each block in a fork adds weight from its proposer's attestation. A longer\n fork has more accumulated weight from the proposers along its length.\n\n This is how the protocol ensures liveness: the chain that continues to grow\n (accumulating blocks and attestations) becomes the canonical chain.", + "description": "Fork choice handles deep fork splits correctly.\n\nScenario\n--------\nCreate two forks that diverge at slot 2 and extend to different depths.\n\nExpected Behavior:\n - Fork A extends to slot 4\n - Fork B extends to slot 5\n - Head follows the longer (heavier) fork B\n\nWhy This Matters\n----------------\nIn practice, forks can persist for multiple slots before one gains dominance.\nThis tests that fork choice correctly follows the deeper fork, which has\naccumulated more proposer attestations along its chain.\n\nEach block in a fork adds weight from its proposer's attestation. A longer\nfork has more accumulated weight from the proposers along its length.\n\nThis is how the protocol ensures liveness: the chain that continues to grow\n(accumulating blocks and attestations) becomes the canonical chain.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json index 7a09942..4fcdc45 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_gaps_in_slots.json @@ -263,7 +263,7 @@ "hash": "0x1c4e74137050dc0c7edf1fd097829cad26332dcb8f90d7981b04881411970625", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_gaps_in_slots[fork_Devnet]", - "description": "Fork choice head handles missing slots correctly.\n\n Scenario\n --------\n Build blocks at slots 1, 3, 5, 7, 9 (skipping even slots).\n\n Expected Behavior:\n - Head advances to each present block\n - Skipped slots don't affect fork choice\n - Head correctly identifies the leaf despite gaps\n\n Why This Matters\n ----------------\n Missed slots are common in production:\n - Offline proposers\n - Network partitions\n - Proposer failures\n\n Fork choice must handle sparse block production correctly. The algorithm\n doesn't require consecutive slots - it works with any tree structure where\n gaps are simply missing nodes.\n\n This verifies the algorithm handles real-world conditions where not every\n slot has a block, which is the norm rather than the exception.", + "description": "Fork choice head handles missing slots correctly.\n\nScenario\n--------\nBuild blocks at slots 1, 3, 5, 7, 9 (skipping even slots).\n\nExpected Behavior:\n - Head advances to each present block\n - Skipped slots don't affect fork choice\n - Head correctly identifies the leaf despite gaps\n\nWhy This Matters\n----------------\nMissed slots are common in production:\n- Offline proposers\n- Network partitions\n- Proposer failures\n\nFork choice must handle sparse block production correctly. The algorithm\ndoesn't require consecutive slots - it works with any tree structure where\ngaps are simply missing nodes.\n\nThis verifies the algorithm handles real-world conditions where not every\nslot has a block, which is the norm rather than the exception.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json index 17e905c..4342f1d 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_large_gaps.json @@ -225,7 +225,7 @@ "hash": "0x3535bcb8a480fe5f76ad58fcb8aa539c082838ef70fa57b9f7f92ada56f0520b", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_large_gaps[fork_Devnet]", - "description": "Fork choice head handles large gaps between blocks.\n\n Scenario\n --------\n Build blocks at slots 1, 10, 20, 30 (gaps of 9-10 slots).\n\n Expected Behavior:\n - Head advances despite large gaps\n - Fork choice is gap-size independent\n - Head reaches the furthest block\n\n Why This Matters\n ----------------\n Large gaps can occur during:\n - Extended network partitions\n - Chain reorganizations\n - Periods of high validator downtime\n - Initial sync after being offline\n\n The fork choice algorithm must remain correct regardless of gap size.\n Distance between blocks should not affect the correctness of head selection -\n only the tree structure matters.\n\n This test verifies that even with dramatic gaps (representing severe network\n conditions), fork choice still identifies the correct head.", + "description": "Fork choice head handles large gaps between blocks.\n\nScenario\n--------\nBuild blocks at slots 1, 10, 20, 30 (gaps of 9-10 slots).\n\nExpected Behavior:\n - Head advances despite large gaps\n - Fork choice is gap-size independent\n - Head reaches the furthest block\n\nWhy This Matters\n----------------\nLarge gaps can occur during:\n- Extended network partitions\n- Chain reorganizations\n- Periods of high validator downtime\n- Initial sync after being offline\n\nThe fork choice algorithm must remain correct regardless of gap size.\nDistance between blocks should not affect the correctness of head selection -\nonly the tree structure matters.\n\nThis test verifies that even with dramatic gaps (representing severe network\nconditions), fork choice still identifies the correct head.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json index e55e9e2..c692b89 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_head/test_head_with_two_competing_forks.json @@ -196,7 +196,7 @@ "hash": "0x4bc2b45ff4760cad0fd489f241759c5452e550dca1f4cab25d47818c5156cc69", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_head.py::test_head_with_two_competing_forks[fork_Devnet]", - "description": "Fork choice selects head when two forks compete at the same slot.\n\n Scenario\n --------\n Create two competing blocks at slot 2, both building on slot 1.\n\n Expected Behavior:\n - After slot 1: head = slot 1 (common ancestor)\n - After fork A (slot 2): head = slot 2 (fork A, first seen)\n - After fork B (slot 2): head = slot 2 (still fork A)\n - Both forks have equal weight (1 proposer attestation each)\n - Head breaks tie lexicographically by block root\n\n Why This Matters\n ----------------\n This is an important fork choice scenario: two blocks competing for the\n same slot. Even with equal attestation weight, fork choice must deterministically\n select a head.\n\n The algorithm uses lexicographic order of block roots as a tie-breaker,\n ensuring all nodes agree on the same head even when forks have equal weight.\n\n This prevents network splits and ensures consensus converges.", + "description": "Fork choice selects head when two forks compete at the same slot.\n\nScenario\n--------\nCreate two competing blocks at slot 2, both building on slot 1.\n\nExpected Behavior:\n - After slot 1: head = slot 1 (common ancestor)\n - After fork A (slot 2): head = slot 2 (fork A, first seen)\n - After fork B (slot 2): head = slot 2 (still fork A)\n - Both forks have equal weight (1 proposer attestation each)\n - Head breaks tie lexicographically by block root\n\nWhy This Matters\n----------------\nThis is an important fork choice scenario: two blocks competing for the\nsame slot. Even with equal attestation weight, fork choice must deterministically\nselect a head.\n\nThe algorithm uses lexicographic order of block roots as a tie-breaker,\nensuring all nodes agree on the same head even when forks have equal weight.\n\nThis prevents network splits and ensures consensus converges.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json index a185a6c..7d1cfc0 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_back_and_forth_reorg_oscillation.json @@ -409,7 +409,7 @@ "hash": "0x6eb660974d0dd40d27c2e4a5eaeb5d72114f7a4fd70572ea1876f4a3ff08ec44", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_back_and_forth_reorg_oscillation[fork_Devnet]", - "description": "Multiple reorgs as two forks alternately extend (pathological case).\n\n Scenario\n --------\n Two forks alternate extensions, causing head to oscillate back and forth.\n This is a pathological case that shouldn't happen in healthy networks but\n tests fork choice correctness under extreme conditions.\n\n Oscillation Pattern:\n Slot 2: Fork A leads (1 vs 0) \u2190 head\n Slot 2: Fork B created (1 vs 1) \u2192 tie, A maintains\n Slot 3: Fork B extends (2 vs 1) \u2190 head switches to B (REORG #1)\n Slot 3: Fork A extends (2 vs 2) \u2192 tie, B maintains\n Slot 4: Fork A extends (3 vs 2) \u2190 head switches to A (REORG #2)\n Slot 4: Fork B extends (3 vs 3) \u2192 tie, A maintains\n Slot 5: Fork B extends (4 vs 3) \u2190 head switches to B (REORG #3)\n\n Expected Behavior\n -----------------\n 1. Head oscillates: A \u2192 B \u2192 A \u2192 B\n 2. Each extension triggers reorg to that fork\n 3. All reorgs are 1-2 blocks deep\n 4. Fork choice remains consistent and correct throughout\n\n Reorg Count: 3 reorgs in 4 slots (very high rate)\n\n Why This Matters\n ----------------\n While extremely rare, this scenario can theoretically occur:\n - Two validator groups in different network segments\n - Each group primarily seeing their own fork first\n - Alternating proposer selection between groups\n - High network latency preventing convergence\n\n Properties Tested:\n - Fork choice handles rapid reorg sequences\n - No state corruption despite frequent head changes\n - Tie-breaking remains consistent\n - Weight calculation correct after multiple reorgs\n - System eventually stabilizes to heaviest fork\n\n This stress test verifies robustness under worst-case fork competition,\n ensuring the protocol remains safe even in pathological network conditions.\n In practice, networks self-heal from such scenarios through attestation\n convergence.", + "description": "Multiple reorgs as two forks alternately extend (pathological case).\n\nScenario\n--------\nTwo forks alternate extensions, causing head to oscillate back and forth.\nThis is a pathological case that shouldn't happen in healthy networks but\ntests fork choice correctness under extreme conditions.\n\nOscillation Pattern:\n Slot 2: Fork A leads (1 vs 0) \u2190 head\n Slot 2: Fork B created (1 vs 1) \u2192 tie, A maintains\n Slot 3: Fork B extends (2 vs 1) \u2190 head switches to B (REORG #1)\n Slot 3: Fork A extends (2 vs 2) \u2192 tie, B maintains\n Slot 4: Fork A extends (3 vs 2) \u2190 head switches to A (REORG #2)\n Slot 4: Fork B extends (3 vs 3) \u2192 tie, A maintains\n Slot 5: Fork B extends (4 vs 3) \u2190 head switches to B (REORG #3)\n\nExpected Behavior\n-----------------\n1. Head oscillates: A \u2192 B \u2192 A \u2192 B\n2. Each extension triggers reorg to that fork\n3. All reorgs are 1-2 blocks deep\n4. Fork choice remains consistent and correct throughout\n\nReorg Count: 3 reorgs in 4 slots (very high rate)\n\nWhy This Matters\n----------------\nWhile extremely rare, this scenario can theoretically occur:\n- Two validator groups in different network segments\n- Each group primarily seeing their own fork first\n- Alternating proposer selection between groups\n- High network latency preventing convergence\n\nProperties Tested:\n- Fork choice handles rapid reorg sequences\n- No state corruption despite frequent head changes\n- Tie-breaking remains consistent\n- Weight calculation correct after multiple reorgs\n- System eventually stabilizes to heaviest fork\n\nThis stress test verifies robustness under worst-case fork competition,\nensuring the protocol remains safe even in pathological network conditions.\nIn practice, networks self-heal from such scenarios through attestation\nconvergence.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json index e8a4570..4e27273 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_on_newly_justified_slot.json @@ -373,7 +373,7 @@ "hash": "0x66b6b80a4e57f934e8c1a3c738abb1a3af78e66b933d7856eb9c4cc7eac3d160", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_on_newly_justified_slot[fork_Devnet]", - "description": "Reorg occurs correctly when forks cross justification boundaries.\n\n Scenario\n --------\n Two forks compete. Fork A is heavier and longer, but Fork B manages to\n become justified. Fork choice must switch to the justified fork regardless\n of weight/length.\n\n - Slot 1: Base\n - Slots 2-4: Fork A extends (becomes head with depth 3)\n - Slot 5: Fork B appears (descending from Base, skipping slots 2-4)\n - Slot 6: Fork B extends. This block contains enough attestations to\n justify Fork B at Slot 5.\n\n Expected Behavior\n -----------------\n 1. Fork A takes the lead initially (Slots 2-4) as the heaviest chain.\n 2. Fork B appears at Slot 5 but is initially lighter.\n 3. At Slot 6, the new block includes attestations that justify Fork B at Slot 5.\n 4. The justified checkpoint updates to Slot 5 (fork_b_1).\n 5. Fork A is immediately discarded because it does not descend from the new\n justified checkpoint (Fork A is on a branch from Slot 1).\n 6. Fork B becomes the canonical head.\n\n Why This Matters\n ----------------\n Justification is a critical safety mechanism:\n - Limits which blocks can be attested to\n - Ensures fork choice respects finality constraints\n\n This test ensures:\n - Reorgs respect justification boundaries\n - Fork choice works correctly across justifiable slots\n - Safety guarantees maintained during reorgs", + "description": "Reorg occurs correctly when forks cross justification boundaries.\n\nScenario\n--------\nTwo forks compete. Fork A is heavier and longer, but Fork B manages to\nbecome justified. Fork choice must switch to the justified fork regardless\nof weight/length.\n\n- Slot 1: Base\n- Slots 2-4: Fork A extends (becomes head with depth 3)\n- Slot 5: Fork B appears (descending from Base, skipping slots 2-4)\n- Slot 6: Fork B extends. This block contains enough attestations to\n justify Fork B at Slot 5.\n\nExpected Behavior\n-----------------\n1. Fork A takes the lead initially (Slots 2-4) as the heaviest chain.\n2. Fork B appears at Slot 5 but is initially lighter.\n3. At Slot 6, the new block includes attestations that justify Fork B at Slot 5.\n4. The justified checkpoint updates to Slot 5 (fork_b_1).\n5. Fork A is immediately discarded because it does not descend from the new\n justified checkpoint (Fork A is on a branch from Slot 1).\n6. Fork B becomes the canonical head.\n\nWhy This Matters\n----------------\nJustification is a critical safety mechanism:\n- Limits which blocks can be attested to\n- Ensures fork choice respects finality constraints\n\nThis test ensures:\n- Reorgs respect justification boundaries\n- Fork choice works correctly across justifiable slots\n- Safety guarantees maintained during reorgs", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json index 0af1bc4..fdb51a8 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_prevention_heavy_fork_resists_light_competition.json @@ -474,7 +474,7 @@ "hash": "0x6023413932cda4e5e286bbb77214e99ba600e6e8eb048233ab87648250b9fb56", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_prevention_heavy_fork_resists_light_competition[fork_Devnet]", - "description": "Established heavy fork successfully resists light competing fork.\n\n Scenario\n --------\n - Fork A builds substantial lead (5 blocks)\n - Fork B created late, builds 3 blocks\n - Fork A maintains head despite fork B's growth\n\n Chain Evolution:\n Slots 1-5: Fork A builds uncontested (5 blocks)\n Slot 6: Fork B starts from slot 1 (late competitor)\n Slots 6-8: Fork B builds 3 blocks (total 3 vs fork A's 5)\n Result: Fork A remains canonical (reorg prevented)\n\n Expected Behavior\n -----------------\n 1. Fork A establishes 5-block lead\n 2. Fork B starts competing from an earlier slot\n 3. Fork B builds rapidly but can't match fork A's depth\n 4. Head remains on fork A throughout (no reorg)\n\n Why This Matters\n ----------------\n Reorg resistance is crucial for chain stability:\n - Prevents cheap disruption of established chain\n - Requires substantial work to overtake canonical fork\n - Protects against late-arriving competing forks\n - Ensures finality can eventually be reached\n\n Attack Prevention:\n - Attacker can't easily reorg established blocks\n - Must match or exceed weight of canonical chain\n - Time advantage gives canonical chain strong position\n - Network naturally converges on heaviest fork", + "description": "Established heavy fork successfully resists light competing fork.\n\nScenario\n--------\n- Fork A builds substantial lead (5 blocks)\n- Fork B created late, builds 3 blocks\n- Fork A maintains head despite fork B's growth\n\nChain Evolution:\n Slots 1-5: Fork A builds uncontested (5 blocks)\n Slot 6: Fork B starts from slot 1 (late competitor)\n Slots 6-8: Fork B builds 3 blocks (total 3 vs fork A's 5)\n Result: Fork A remains canonical (reorg prevented)\n\nExpected Behavior\n-----------------\n1. Fork A establishes 5-block lead\n2. Fork B starts competing from an earlier slot\n3. Fork B builds rapidly but can't match fork A's depth\n4. Head remains on fork A throughout (no reorg)\n\nWhy This Matters\n----------------\nReorg resistance is crucial for chain stability:\n- Prevents cheap disruption of established chain\n- Requires substantial work to overtake canonical fork\n- Protects against late-arriving competing forks\n- Ensures finality can eventually be reached\n\nAttack Prevention:\n- Attacker can't easily reorg established blocks\n- Must match or exceed weight of canonical chain\n- Time advantage gives canonical chain strong position\n- Network naturally converges on heaviest fork", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json index 6c8465a..4be5081 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_reorg_with_slot_gaps.json @@ -353,7 +353,7 @@ "hash": "0x5f6aaad85e060d9668421962be832a54173204493c8b740d6b960502deab04ad", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_reorg_with_slot_gaps[fork_Devnet]", - "description": "Reorg occurs correctly even with missed slots in the chain.\n\n Scenario\n --------\n - Slot 1: Base\n - Slot 3: Fork A (skipping slot 2)\n - Slot 4: Fork B (competing)\n - Slot 7: Fork A extended (skipping slots 4-6)\n - Slot 8: Fork B extended (skipping slots 5-7)\n - Slot 9: Fork B extended again \u2192 triggers reorg\n\n Missed Slots: 2, 5, 6 (no blocks produced)\n\n Expected Behavior\n -----------------\n 1. Sparse block production doesn't affect fork choice logic\n 2. Weight calculation only considers actual blocks\n 3. Reorg happens based on block count, not slot numbers\n 4. Fork B with 3 blocks beats fork A with 2 blocks\n\n Reorg Details:\n - **Depth**: 2 blocks (fork_a slots 3, 7)\n - **Trigger**: Progressive building despite gaps\n - **Weight**: 3 proposer attestations vs 2\n\n Why This Matters\n ----------------\n Missed slots are extremely common in production:\n - Offline validators (expected ~1% downtime)\n - Network issues preventing timely block propagation\n - Intentional skips during network congestion\n\n Fork choice must remain robust with sparse block production:\n - Gaps don't create bias toward any fork\n - Only actual blocks contribute weight\n - Reorg logic works identically whether slots are consecutive or sparse\n\n This test ensures the algorithm works correctly in realistic network\n conditions where perfect block production is impossible.", + "description": "Reorg occurs correctly even with missed slots in the chain.\n\nScenario\n--------\n- Slot 1: Base\n- Slot 3: Fork A (skipping slot 2)\n- Slot 4: Fork B (competing)\n- Slot 7: Fork A extended (skipping slots 4-6)\n- Slot 8: Fork B extended (skipping slots 5-7)\n- Slot 9: Fork B extended again \u2192 triggers reorg\n\nMissed Slots: 2, 5, 6 (no blocks produced)\n\nExpected Behavior\n-----------------\n1. Sparse block production doesn't affect fork choice logic\n2. Weight calculation only considers actual blocks\n3. Reorg happens based on block count, not slot numbers\n4. Fork B with 3 blocks beats fork A with 2 blocks\n\nReorg Details:\n - **Depth**: 2 blocks (fork_a slots 3, 7)\n - **Trigger**: Progressive building despite gaps\n - **Weight**: 3 proposer attestations vs 2\n\nWhy This Matters\n----------------\nMissed slots are extremely common in production:\n- Offline validators (expected ~1% downtime)\n- Network issues preventing timely block propagation\n- Intentional skips during network congestion\n\nFork choice must remain robust with sparse block production:\n- Gaps don't create bias toward any fork\n- Only actual blocks contribute weight\n- Reorg logic works identically whether slots are consecutive or sparse\n\nThis test ensures the algorithm works correctly in realistic network\nconditions where perfect block production is impossible.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json index ee46e54..0578fa9 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_simple_one_block_reorg.json @@ -237,7 +237,7 @@ "hash": "0x7d680f28b0ece5d50dbda5690d6bfa99bdcc6437b8eab75ecd943038d6207d89", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_simple_one_block_reorg[fork_Devnet]", - "description": "Simplest reorg: one-block fork overtakes another via extension.\n\n Scenario\n --------\n - Slot 1: Common ancestor (chain_base)\n - Slot 2: Fork A created, becomes head\n - Slot 2: Fork B created (competing fork at same slot)\n - Slot 3: Fork B extended \u2192 triggers reorg from A to B\n\n Expected Behavior\n -----------------\n 1. After fork_a_2: head = fork_a_2 (first fork created)\n 2. After fork_b_2: head = fork_a_2 (equal weight, head remains unchanged)\n 3. After fork_b_3: head = fork_b_3 (fork B heavier due to extension)\n\n Reorg Details:\n - **Depth**: 1 block (fork_a_2 becomes non-canonical)\n - **Trigger**: Fork extension (proposer attestation)\n - **Weight advantage**: Fork B has 2 proposer attestations vs 1\n\n Why This Matters\n ----------------\n This is the most common reorg scenario in practice:\n - Two blocks proposed at nearly the same time\n - Network temporarily splits (half see A first, half see B first)\n - Next proposer builds on one fork, resolving the split\n - Fork choice converges to the extended fork\n\n Tests the fundamental property: extending a fork makes it heavier.", + "description": "Simplest reorg: one-block fork overtakes another via extension.\n\nScenario\n--------\n- Slot 1: Common ancestor (chain_base)\n- Slot 2: Fork A created, becomes head\n- Slot 2: Fork B created (competing fork at same slot)\n- Slot 3: Fork B extended \u2192 triggers reorg from A to B\n\nExpected Behavior\n-----------------\n1. After fork_a_2: head = fork_a_2 (first fork created)\n2. After fork_b_2: head = fork_a_2 (equal weight, head remains unchanged)\n3. After fork_b_3: head = fork_b_3 (fork B heavier due to extension)\n\nReorg Details:\n - **Depth**: 1 block (fork_a_2 becomes non-canonical)\n - **Trigger**: Fork extension (proposer attestation)\n - **Weight advantage**: Fork B has 2 proposer attestations vs 1\n\nWhy This Matters\n----------------\nThis is the most common reorg scenario in practice:\n- Two blocks proposed at nearly the same time\n- Network temporarily splits (half see A first, half see B first)\n- Next proposer builds on one fork, resolving the split\n- Fork choice converges to the extended fork\n\nTests the fundamental property: extending a fork makes it heavier.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json index 2164312..13c76db 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_block_deep_reorg.json @@ -409,7 +409,7 @@ "hash": "0x6c0c877f128860007e089317a158d18dd3434128978b9c456983066c9e5ec413", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_block_deep_reorg[fork_Devnet]", - "description": "Deep three-block reorg from established fork to alternative.\n\n Scenario\n --------\n - Slot 1: Common base\n - Slots 2-4: Fork A builds 3-block lead\n - Slots 2-5: Fork B slowly builds, then surpasses with 4 blocks\n\n Timeline:\n Slot 2: Fork A leads (1 vs 0)\n Slot 3: Fork A leads (2 vs 1)\n Slot 4: Fork A leads (3 vs 2)\n Slot 5: Fork B overtakes (4 vs 3) \u2192 3-block deep reorg\n\n Expected Behavior\n -----------------\n 1. Fork A establishes 3-block canonical chain (slots 2-4)\n 2. Fork B steadily builds parallel chain\n 3. At slot 5, fork B has 4 blocks vs fork A's 3 blocks\n 4. Fork choice switches to fork B\n 5. Three blocks (fork_a slots 2-4) become non-canonical\n\n Reorg Details:\n - **Depth**: 3 blocks (deepest in this test suite)\n - **Trigger**: Alternative fork becomes longer\n\n Why This Matters\n ----------------\n Deep reorgs (3+ blocks) are rare in healthy networks but can happen:\n - Network partitions lasting multiple slots\n - Coordinated validator behavior (intentional or accidental)\n - Major network latency events\n\n Properties verified:\n - Fork choice correctly switches even after multiple canonical blocks\n - Weight calculation works correctly over extended depth\n - No \"stickiness\" bias toward existing head\n - Objective heaviest fork always wins\n\n This tests the protocol's ability to recover from significant disagreement\n about chain history, ensuring safety and liveness even in adversarial scenarios.", + "description": "Deep three-block reorg from established fork to alternative.\n\nScenario\n--------\n- Slot 1: Common base\n- Slots 2-4: Fork A builds 3-block lead\n- Slots 2-5: Fork B slowly builds, then surpasses with 4 blocks\n\nTimeline:\n Slot 2: Fork A leads (1 vs 0)\n Slot 3: Fork A leads (2 vs 1)\n Slot 4: Fork A leads (3 vs 2)\n Slot 5: Fork B overtakes (4 vs 3) \u2192 3-block deep reorg\n\nExpected Behavior\n-----------------\n1. Fork A establishes 3-block canonical chain (slots 2-4)\n2. Fork B steadily builds parallel chain\n3. At slot 5, fork B has 4 blocks vs fork A's 3 blocks\n4. Fork choice switches to fork B\n5. Three blocks (fork_a slots 2-4) become non-canonical\n\nReorg Details:\n - **Depth**: 3 blocks (deepest in this test suite)\n - **Trigger**: Alternative fork becomes longer\n\nWhy This Matters\n----------------\nDeep reorgs (3+ blocks) are rare in healthy networks but can happen:\n- Network partitions lasting multiple slots\n- Coordinated validator behavior (intentional or accidental)\n- Major network latency events\n\nProperties verified:\n- Fork choice correctly switches even after multiple canonical blocks\n- Weight calculation works correctly over extended depth\n- No \"stickiness\" bias toward existing head\n- Objective heaviest fork always wins\n\nThis tests the protocol's ability to recover from significant disagreement\nabout chain history, ensuring safety and liveness even in adversarial scenarios.", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json index 47ce227..9e1e427 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_three_way_fork_competition.json @@ -360,7 +360,7 @@ "hash": "0xac7885eee02fff5701e651238f6ab4dd7d0a4ce24bae0fc32bb145c3f0c823ea", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_three_way_fork_competition[fork_Devnet]", - "description": "Three competing forks with progressive elimination until one wins.\n\n Scenario\n --------\n Three forks (A, B, C) compete simultaneously. Fork choice progressively\n eliminates weaker forks as stronger ones extend.\n\n Fork Topology:\n base (slot 1)\n / | / | fork_a fork_b fork_c (slot 2)\n | | |\n | | +--- fork_c_3 (slot 3)\n | +--- fork_b_3 (slot 3)\n | +--- fork_b_4 (slot 4) \u2190 Winner\n +--- abandoned\n\n Expected Behavior\n -----------------\n 1. All three forks start at slot 2 (three-way tie)\n 2. Fork C extends to slot 3 \u2192 becomes head\n 3. Fork B extends to slot 3 \u2192 ties with fork C at depth 2\n 4. Fork B extends to slot 4 \u2192 wins with depth 3\n 5. Forks A and C become non-canonical\n\n Reorg Sequence:\n - Initial: fork_a (tie-breaker among three)\n - After fork_c_3: fork_c (depth advantage)\n - After fork_b_3: fork_c (tie, maintains head)\n - After fork_b_4: fork_b (final winner)\n\n Why This Matters\n ----------------\n Multi-fork scenarios can occur during:\n - Network partitions splitting validators 3+ ways\n - Rapid block production creating multiple conflicting proposals\n - Byzantine validators intentionally creating competing forks\n\n Properties verified:\n - Fork choice handles 3+ simultaneous competing forks\n - Head selection remains consistent and deterministic\n - Progressive elimination works correctly\n - Final winner is objectively the heaviest fork", + "description": "Three competing forks with progressive elimination until one wins.\n\nScenario\n--------\nThree forks (A, B, C) compete simultaneously. Fork choice progressively\neliminates weaker forks as stronger ones extend.\n\nFork Topology:\n base (slot 1)\n / | / | fork_a fork_b fork_c (slot 2)\n | | |\n | | +--- fork_c_3 (slot 3)\n | +--- fork_b_3 (slot 3)\n | +--- fork_b_4 (slot 4) \u2190 Winner\n +--- abandoned\n\nExpected Behavior\n-----------------\n1. All three forks start at slot 2 (three-way tie)\n2. Fork C extends to slot 3 \u2192 becomes head\n3. Fork B extends to slot 3 \u2192 ties with fork C at depth 2\n4. Fork B extends to slot 4 \u2192 wins with depth 3\n5. Forks A and C become non-canonical\n\nReorg Sequence:\n - Initial: fork_a (tie-breaker among three)\n - After fork_c_3: fork_c (depth advantage)\n - After fork_b_3: fork_c (tie, maintains head)\n - After fork_b_4: fork_b (final winner)\n\nWhy This Matters\n----------------\nMulti-fork scenarios can occur during:\n- Network partitions splitting validators 3+ ways\n- Rapid block production creating multiple conflicting proposals\n- Byzantine validators intentionally creating competing forks\n\nProperties verified:\n- Fork choice handles 3+ simultaneous competing forks\n- Head selection remains consistent and deterministic\n- Progressive elimination works correctly\n- Final winner is objectively the heaviest fork", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json index 3573d0c..29523e3 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_fork_choice_reorgs/test_two_block_reorg_progressive_building.json @@ -319,7 +319,7 @@ "hash": "0x116a3893f44e2058a7530eb22611f98a86923aa25541cc9f509fbb758ecb1012", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_fork_choice_reorgs.py::test_two_block_reorg_progressive_building[fork_Devnet]", - "description": "Two-block reorg via progressive fork building.\n\n Scenario\n --------\n - Slot 1: Common ancestor\n - Slots 2-3: Fork A extends to 2 blocks ahead\n - Slots 2-4: Fork B slowly catches up, then overtakes\n\n Chain State Evolution:\n Slot 1: base\n Slot 2: base \u2190 fork_a_2 (head)\n base \u2190 fork_b_2\n Slot 3: base \u2190 fork_a_2 \u2190 fork_a_3 (head)\n base \u2190 fork_b_2\n Slot 4: base \u2190 fork_a_2 \u2190 fork_a_3 (was head)\n base \u2190 fork_b_2 \u2190 fork_b_3 (tie at depth 2)\n Slot 5: base \u2190 fork_a_2 \u2190 fork_a_3 (abandoned)\n base \u2190 fork_b_2 \u2190 fork_b_3 \u2190 fork_b_4 (head - REORG!)\n\n Expected Behavior\n -----------------\n 1. Fork A leads for slots 2-3 (2 blocks ahead)\n 2. Fork B catches up at slot 4 (both at depth 2)\n 3. Fork B overtakes at slot 5 (3 blocks vs 2)\n 4. Two-block reorg: fork_a_2 and fork_a_3 become non-canonical\n\n Reorg Details:\n - **Depth**: 2 blocks\n - **Trigger**: Progressive building on alternative fork\n - **Weight advantage**: Fork B has 3 proposer attestations vs 2\n\n Why This Matters\n ----------------\n Demonstrates that an initially leading fork can be overtaken if:\n - Proposers switch to building on the alternative fork\n - The alternative fork accumulates more blocks over time\n - Network temporarily favored one fork but consensus shifted", + "description": "Two-block reorg via progressive fork building.\n\nScenario\n--------\n- Slot 1: Common ancestor\n- Slots 2-3: Fork A extends to 2 blocks ahead\n- Slots 2-4: Fork B slowly catches up, then overtakes\n\nChain State Evolution:\n Slot 1: base\n Slot 2: base \u2190 fork_a_2 (head)\n base \u2190 fork_b_2\n Slot 3: base \u2190 fork_a_2 \u2190 fork_a_3 (head)\n base \u2190 fork_b_2\n Slot 4: base \u2190 fork_a_2 \u2190 fork_a_3 (was head)\n base \u2190 fork_b_2 \u2190 fork_b_3 (tie at depth 2)\n Slot 5: base \u2190 fork_a_2 \u2190 fork_a_3 (abandoned)\n base \u2190 fork_b_2 \u2190 fork_b_3 \u2190 fork_b_4 (head - REORG!)\n\nExpected Behavior\n-----------------\n1. Fork A leads for slots 2-3 (2 blocks ahead)\n2. Fork B catches up at slot 4 (both at depth 2)\n3. Fork B overtakes at slot 5 (3 blocks vs 2)\n4. Two-block reorg: fork_a_2 and fork_a_3 become non-canonical\n\nReorg Details:\n - **Depth**: 2 blocks\n - **Trigger**: Progressive building on alternative fork\n - **Weight advantage**: Fork B has 3 proposer attestations vs 2\n\nWhy This Matters\n----------------\nDemonstrates that an initially leading fork can be overtaken if:\n- Proposers switch to building on the alternative fork\n- The alternative fork accumulates more blocks over time\n- Network temporarily favored one fork but consensus shifted", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json b/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json index bc7d1b1..c73baf7 100644 --- a/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json +++ b/lean_client/test_vectors/fork_choice/devnet/fc/test_lexicographic_tiebreaker/test_equal_weight_forks_use_lexicographic_tiebreaker.json @@ -280,7 +280,7 @@ "hash": "0x8aba9aa539e3d29ad352b5387f2706296b6884eef70ce5ddf4cc89bbe403223c", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/fc/test_lexicographic_tiebreaker.py::test_equal_weight_forks_use_lexicographic_tiebreaker[fork_Devnet]", - "description": "Fork choice selects lexicographically highest branch when fork weights tie.\n\n Scenario\n --------\n - Slot 1: Build common ancestor\n - Slots 2-3: Build fork A to depth 2 (slots 2 & 3)\n - Slots 2-3: Build fork B to depth 2 (slots 2 & 3)\n\n Both forks have identical structure:\n - Same depth (2 blocks each)\n - Same attestation weight (2 proposer attestations each)\n - Same parent (common ancestor at slot 1)\n\n Expected Behavior\n -----------------\n The competing forks have identical attestation weight. The head is chosen\n via lexicographic ordering of the block roots. The framework automatically\n verifies that:\n 1. Both forks are at the same slot (equal depth)\n 2. The head is the lexicographically highest root among them", + "description": "Fork choice selects lexicographically highest branch when fork weights tie.\n\nScenario\n--------\n- Slot 1: Build common ancestor\n- Slots 2-3: Build fork A to depth 2 (slots 2 & 3)\n- Slots 2-3: Build fork B to depth 2 (slots 2 & 3)\n\nBoth forks have identical structure:\n- Same depth (2 blocks each)\n- Same attestation weight (2 proposer attestations each)\n- Same parent (common ancestor at slot 1)\n\nExpected Behavior\n-----------------\nThe competing forks have identical attestation weight. The head is chosen\nvia lexicographic ordering of the block roots. The framework automatically\nverifies that:\n1. Both forks are at the same slot (equal depth)\n2. The head is the lexicographically highest root among them", "fixtureFormat": "fork_choice_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json index 316b1f8..d47ed3e 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_at_large_slot_number.json @@ -75,7 +75,7 @@ "hash": "0x6bf12284f3bcd59681e7d859a95364939a7503d532bf5111bdc7f05bbb8792f5", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_at_large_slot_number[fork_Devnet]", - "description": "Test block processing at high slot numbers.\n\n Scenario\n --------\n Jump directly from genesis to slot 100, simulating:\n - Network bootstrap after long downtime\n - Test environment with artificial time jump\n - Integer overflow boundary testing\n\n Expected Behavior\n -----------------\n 1. Process 99 empty slots: 1\u21922\u2192...\u219299\u2192100\n 2. Block at slot 100 processes correctly\n 3. No integer overflow or wraparound\n 4. State remains consistent", + "description": "Test block processing at high slot numbers.\n\nScenario\n--------\nJump directly from genesis to slot 100, simulating:\n- Network bootstrap after long downtime\n- Test environment with artificial time jump\n- Integer overflow boundary testing\n\nExpected Behavior\n-----------------\n1. Process 99 empty slots: 1\u21922\u2192...\u219299\u2192100\n2. Block at slot 100 processes correctly\n3. No integer overflow or wraparound\n4. State remains consistent", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json index efab07d..d18436d 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_extends_deep_chain.json @@ -284,7 +284,7 @@ "hash": "0x44fac8af1f01ab97fcd1e350bb4906fa7d44bf57121fbf6e7100dd7ed4a37ea9", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_extends_deep_chain[fork_Devnet]", - "description": "Test that blocks can extend already-deep chains.\n\n Scenario\n --------\n Build a 20-block chain to simulate a mature blockchain state,\n then verify new blocks can still extend it correctly.\n\n Expected Behavior\n -----------------\n 1. All 20 blocks process successfully\n 2. Parent linkage maintained throughout\n 3. State advances to slot 20\n 4. Historical roots accumulate correctly\n 5. No degradation in processing", + "description": "Test that blocks can extend already-deep chains.\n\nScenario\n--------\nBuild a 20-block chain to simulate a mature blockchain state,\nthen verify new blocks can still extend it correctly.\n\nExpected Behavior\n-----------------\n1. All 20 blocks process successfully\n2. Parent linkage maintained throughout\n3. State advances to slot 20\n4. Historical roots accumulate correctly\n5. No degradation in processing", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json index 67787ed..48a03a2 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_parent_root.json @@ -73,7 +73,7 @@ "hash": "0xb59ea28148b116cc01bfa07ab28357c75e6b741b4558e402ff35b692310fafdf", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_parent_root[fork_Devnet]", - "description": "Test that blocks with wrong parent root are rejected.\n\n Scenario\n --------\n Attempt to process a block where parent root doesn't match\n hash_tree_root(state.latest block header).\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Block parent root mismatch\"\n\n Why This Matters\n ----------------\n Maintains chain integrity:\n - Blocks must reference correct parent\n - Prevents chain history forgery\n - Ensures linear chain continuity\n - Critical for fork resolution\n\n Without this check, attackers could create invalid chain branches.", + "description": "Test that blocks with wrong parent root are rejected.\n\nScenario\n--------\nAttempt to process a block where parent root doesn't match\nhash_tree_root(state.latest block header).\n\nExpected Behavior\n-----------------\nBlock processing fails with AssertionError: \"Block parent root mismatch\"\n\nWhy This Matters\n----------------\nMaintains chain integrity:\n- Blocks must reference correct parent\n- Prevents chain history forgery\n- Ensures linear chain continuity\n- Critical for fork resolution\n\nWithout this check, attackers could create invalid chain branches.", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json index 105481b..69c32ff 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_proposer.json @@ -73,7 +73,7 @@ "hash": "0xb45434f975584f6530884cf438a3021c2f609ccb0e32dfa468912ac537384d07", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_proposer[fork_Devnet]", - "description": "Test that blocks from wrong proposer are rejected.\n\n Scenario\n --------\n Attempt to process a block where proposer index doesn't match\n the expected proposer for that slot.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Incorrect block proposer\"\n\n Why This Matters\n ----------------\n Prevents unauthorized block production:\n - Only designated proposer can produce blocks\n - Prevents validator impersonation\n - Maintains protocol security\n - Essential for consensus integrity\n\n Without this check, any validator could produce blocks for any slot.", + "description": "Test that blocks from wrong proposer are rejected.\n\nScenario\n--------\nAttempt to process a block where proposer index doesn't match\nthe expected proposer for that slot.\n\nExpected Behavior\n-----------------\nBlock processing fails with AssertionError: \"Incorrect block proposer\"\n\nWhy This Matters\n----------------\nPrevents unauthorized block production:\n- Only designated proposer can produce blocks\n- Prevents validator impersonation\n- Maintains protocol security\n- Essential for consensus integrity\n\nWithout this check, any validator could produce blocks for any slot.", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json index 66a7bde..4608bc5 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_invalid_state_root.json @@ -73,7 +73,7 @@ "hash": "0xea6849fca98ce01ecd1544f9e0101d6930aaf8e3fe874cd632f2f70a39a9b8d7", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_invalid_state_root[fork_Devnet]", - "description": "Test that blocks with wrong state root commitment are rejected.\n\n Scenario\n --------\n Create a block with state root that doesn't match the actual\n post-state hash.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Invalid block state root\"\n\n Why This Matters\n ----------------\n Cryptographic state commitment is fundamental:\n - Proves correct state execution\n - Prevents state manipulation\n\n This is a critical validation - without it, proposers could claim any arbitrary state.", + "description": "Test that blocks with wrong state root commitment are rejected.\n\nScenario\n--------\nCreate a block with state root that doesn't match the actual\npost-state hash.\n\nExpected Behavior\n-----------------\nBlock processing fails with AssertionError: \"Invalid block state root\"\n\nWhy This Matters\n----------------\nCryptographic state commitment is fundamental:\n- Proves correct state execution\n- Prevents state manipulation\n\nThis is a critical validation - without it, proposers could claim any arbitrary state.", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json index bab1496..34de7e5 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_block_with_wrong_slot.json @@ -74,7 +74,7 @@ "hash": "0x4d79e8bd893bd5e8d42ff3247ba12692b98a5c2cfb2a2c155c9959058f992683", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_block_with_wrong_slot[fork_Devnet]", - "description": "Test that blocks with mismatched slot are rejected.\n\n Scenario\n --------\n Attempt to process a block at slot 1, but the block claims to be\n at slot 2.\n\n Expected Behavior\n -----------------\n Block processing fails with AssertionError: \"Block slot mismatch\"\n\n Why This Matters\n ----------------\n Ensures temporal consistency:\n - Blocks can't lie about their slot\n - Prevents time manipulation attacks\n - Maintains protocol timing integrity\n - Essential for slot-based consensus", + "description": "Test that blocks with mismatched slot are rejected.\n\nScenario\n--------\nAttempt to process a block at slot 1, but the block claims to be\nat slot 2.\n\nExpected Behavior\n-----------------\nBlock processing fails with AssertionError: \"Block slot mismatch\"\n\nWhy This Matters\n----------------\nEnsures temporal consistency:\n- Blocks can't lie about their slot\n- Prevents time manipulation attacks\n- Maintains protocol timing integrity\n- Essential for slot-based consensus", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json index 98a8fcd..e81488f 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_blocks_with_gaps.json @@ -100,7 +100,7 @@ "hash": "0x2bfb5aa59d5cd8287998214476bdf6a689465949bb78e5fd3cba871d3e256588", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_blocks_with_gaps[fork_Devnet]", - "description": "Test blocks separated by empty slots.\n\n Scenario\n --------\n Build chain with gaps:\n - Slot 1: Block\n - Slots 2-3: Empty\n - Slot 4: Block\n - Slots 5-7: Empty\n - Slot 8: Block\n\n Expected Behavior\n -----------------\n 1. Blocks process at specified slots\n 2. Empty slots handled automatically\n 3. Parent linkage spans gaps correctly\n 4. State advances to slot 8\n\n Why This Matters\n ----------------\n Missed proposals are common:\n - Validators offline\n - Network partitions\n - Missed attestations\n\n This validates resilience to gaps.", + "description": "Test blocks separated by empty slots.\n\nScenario\n--------\nBuild chain with gaps:\n- Slot 1: Block\n- Slots 2-3: Empty\n- Slot 4: Block\n- Slots 5-7: Empty\n- Slot 8: Block\n\nExpected Behavior\n-----------------\n1. Blocks process at specified slots\n2. Empty slots handled automatically\n3. Parent linkage spans gaps correctly\n4. State advances to slot 8\n\nWhy This Matters\n----------------\nMissed proposals are common:\n- Validators offline\n- Network partitions\n- Missed attestations\n\nThis validates resilience to gaps.", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json index 085125a..c07abb3 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks.json @@ -132,7 +132,7 @@ "hash": "0x23e331f7f0993188ff0a6dbaeb775f1d132bb748751e8069175659710d5c7860", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks[fork_Devnet]", - "description": "Test processing blocks with empty body (no attestations).\n\n Scenario\n --------\n Build chain of blocks with empty body:\n - Slot 1: Block, Empty body\n - Slot 2: Block, Empty body\n - Slot 3: Block, Empty body\n - Slot 4: Block, Empty body\n - Slot 5: Block, Empty body\n - Slot 6: Block, Empty body\n\n Expected Behavior\n -----------------\n 1. Blocks process as expected\n 2. State advances to slot 6", + "description": "Test processing blocks with empty body (no attestations).\n\nScenario\n--------\nBuild chain of blocks with empty body:\n- Slot 1: Block, Empty body\n- Slot 2: Block, Empty body\n- Slot 3: Block, Empty body\n- Slot 4: Block, Empty body\n- Slot 5: Block, Empty body\n- Slot 6: Block, Empty body\n\nExpected Behavior\n-----------------\n1. Blocks process as expected\n2. State advances to slot 6", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json index 517d3d9..c967597 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_empty_blocks_with_missed_slots.json @@ -121,7 +121,7 @@ "hash": "0x1c7177604eaa86287e779a62115cb4a31c9c40b2a422cb8eff8d9ffd7a113819", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_empty_blocks_with_missed_slots[fork_Devnet]", - "description": "Test processing blocks with empty body (no attestations) combined with missed slots.\n\n Scenario\n --------\n Build chain of blocks with empty body + missed slot:\n - Slot 1: Block\n - Slot 2: Block, Empty body\n - Slot 3: BLock, Empty body\n - Slot 4: Missed\n - Slot 5: Block, Empty body\n - Slot 6: Block\n\n Expected Behavior\n -----------------\n 1. Blocks process at specified slots\n 2. Empty slots handled automatically\n 3. State advances to slot 6", + "description": "Test processing blocks with empty body (no attestations) combined with missed slots.\n\nScenario\n --------\n Build chain of blocks with empty body + missed slot:\n - Slot 1: Block\n - Slot 2: Block, Empty body\n - Slot 3: BLock, Empty body\n - Slot 4: Missed\n - Slot 5: Block, Empty body\n - Slot 6: Block\n\n Expected Behavior\n -----------------\n 1. Blocks process at specified slots\n 2. Empty slots handled automatically\n 3. State advances to slot 6", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json index 6e53e77..55dbe27 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_linear_chain_multiple_blocks.json @@ -119,7 +119,7 @@ "hash": "0xd4848a8416440d7de78e8b422c84061940a6efb0efafa4379f30a1f7a3a2227e", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_linear_chain_multiple_blocks[fork_Devnet]", - "description": "Test building a linear chain of multiple blocks.\n\n Scenario\n --------\n Build a 5-block linear chain:\n genesis \u2192 block1 \u2192 block2 \u2192 block3 \u2192 block4 \u2192 block5\n\n Expected Behavior\n -----------------\n 1. Each block processes in sequence\n 2. Parent linkage maintained throughout\n 3. State advances monotonically\n 4. Historical roots accumulate\n 5. Final state at slot 5", + "description": "Test building a linear chain of multiple blocks.\n\nScenario\n--------\nBuild a 5-block linear chain:\ngenesis \u2192 block1 \u2192 block2 \u2192 block3 \u2192 block4 \u2192 block5\n\nExpected Behavior\n-----------------\n1. Each block processes in sequence\n2. Parent linkage maintained throughout\n3. State advances monotonically\n4. Historical roots accumulate\n5. Final state at slot 5", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json index 49fcb74..ceb1096 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_block_processing/test_process_first_block_after_genesis.json @@ -78,7 +78,7 @@ "hash": "0x0f23fef828567c3abdc5d8b39d55cece7c27c057dbf85294042a78cd535b3e16", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_block_processing.py::test_process_first_block_after_genesis[fork_Devnet]", - "description": "Test processing the first block after genesis.\n\n Scenario\n --------\n Process a single block at slot 1 immediately after genesis.\n\n Expected Behavior\n -----------------\n 1. State advances from slot 0 to slot 1\n 2. Block header is validated and processed\n 3. Latest block header updated to new block\n 4. Historical roots updated with genesis\n 5. Post-state at slot 1\n\n This is the foundation for all subsequent blocks.", + "description": "Test processing the first block after genesis.\n\nScenario\n--------\nProcess a single block at slot 1 immediately after genesis.\n\nExpected Behavior\n-----------------\n1. State advances from slot 0 to slot 1\n2. Block header is validated and processed\n3. Latest block header updated to new block\n4. Historical roots updated with genesis\n5. Post-state at slot 1\n\nThis is the foundation for all subsequent blocks.", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json index bb8e1ff..aeebd23 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_time.json @@ -86,7 +86,7 @@ "hash": "0x7df1a4806954c180abb7472b98c2407e81e060276100b3b67d36c8d11807a1b7", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_time[fork_Devnet]", - "description": "Test genesis state with custom genesis time.\n\n Scenario\n --------\n Generate a genesis state with:\n - genesis_time = 1234567890\n - Default 4 validators\n\n Expected Behavior\n -----------------\n Genesis state should respect the custom genesis time while\n maintaining all other genesis properties.", + "description": "Test genesis state with custom genesis time.\n\nScenario\n--------\nGenerate a genesis state with:\n- genesis_time = 1234567890\n- Default 4 validators\n\nExpected Behavior\n-----------------\nGenesis state should respect the custom genesis time while\nmaintaining all other genesis properties.", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json index c91eee4..9a3e6ef 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_custom_validator_set.json @@ -102,7 +102,7 @@ "hash": "0xce395688e507905dee0a4e0cdd12f1cc4be2ef607c9dbfe80ef2dd5c0d0dff47", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_custom_validator_set[fork_Devnet]", - "description": "Test genesis state with custom validator set.\n\n Scenario\n --------\n Generate a genesis state with:\n - 8 validators instead of default 4\n - Custom validator pubkeys\n\n Expected Behavior\n -----------------\n Genesis state should contain exactly 8 validators while\n maintaining all other genesis properties.", + "description": "Test genesis state with custom validator set.\n\nScenario\n--------\nGenerate a genesis state with:\n- 8 validators instead of default 4\n- Custom validator pubkeys\n\nExpected Behavior\n-----------------\nGenesis state should contain exactly 8 validators while\nmaintaining all other genesis properties.", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json index a2e5a03..297749a 100644 --- a/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json +++ b/lean_client/test_vectors/state_transition/devnet/state_transition/test_genesis/test_genesis_default_configuration.json @@ -86,7 +86,7 @@ "hash": "0xc27d979939715b6ec3c3c5774f3be86ed351b64c73849d39aecd8985ca878e64", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/state_transition/test_genesis.py::test_genesis_default_configuration[fork_Devnet]", - "description": "Test genesis state with default configuration.\n\n Scenario\n --------\n Generate a genesis state with default parameters:\n - genesis_time = 0\n - 4 validators with zero pubkeys", + "description": "Test genesis state with default configuration.\n\nScenario\n--------\nGenerate a genesis state with default parameters:\n- genesis_time = 0\n- 4 validators with zero pubkeys", "fixtureFormat": "state_transition_test" } } diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_aggregated_attestation_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_aggregated_attestation_signature.json new file mode 100644 index 0000000..1ed2abc --- /dev/null +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_aggregated_attestation_signature.json @@ -0,0 +1,1346 @@ +{ + "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_aggregated_attestation_signature[fork_Devnet][fork_devnet-verify_signatures_test]": { + "network": "Devnet", + "leanEnv": "prod", + "anchorState": { + "config": { + "genesisTime": 0 + }, + "slot": 0, + "latestBlockHeader": { + "slot": 0, + "proposerIndex": 0, + "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" + }, + "latestJustified": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "latestFinalized": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "historicalBlockHashes": { + "data": [] + }, + "justifiedSlots": { + "data": [] + }, + "validators": { + "data": [ + { + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", + "index": 0 + }, + { + "pubkey": "0x67047f6113b87f6aea3a0e6c108a8d7dcc4b5e479dda2a1cb79397705340bd6227f1ea63ee39c91fff4599375b749425ae65922e", + "index": 1 + }, + { + "pubkey": "0x235cec31602fa34e41069453a2a56568d4c3ff62e31066659b90722e595e93255637eb0d42f98542e79c60031c2f05396bc74146", + "index": 2 + } + ] + }, + "justificationsRoots": { + "data": [] + }, + "justificationsValidators": { + "data": [] + } + }, + "signedBlockWithAttestation": { + "message": { + "block": { + "slot": 2, + "proposerIndex": 2, + "parentRoot": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", + "stateRoot": "0x617323a572b476eedb950a5fe2b10a6d6d2bd733d2539644c6e744c3c266040d", + "body": { + "attestations": { + "data": [ + { + "aggregationBits": { + "data": [ + true + ] + }, + "data": { + "slot": 2, + "head": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 1 + }, + "target": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 1 + }, + "source": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + } + } + }, + { + "aggregationBits": { + "data": [ + false, + false, + true + ] + }, + "data": { + "slot": 1, + "head": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "target": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "source": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + } + } + } + ] + } + } + }, + "proposerAttestation": { + "validatorId": 2, + "data": { + "slot": 2, + "head": { + "root": "0x08b970a1ffa98e68fbcb06d7208907eef704426764376382a9cb249f8d741b06", + "slot": 2 + }, + "target": { + "root": "0x08b970a1ffa98e68fbcb06d7208907eef704426764376382a9cb249f8d741b06", + "slot": 2 + }, + "source": { + "root": "0x0b530309ddcb6e08a7ddbe1558a772ddea639cec05c2ddff23b597411df0745e", + "slot": 0 + } + } + } + }, + "signature": { + "attestationSignatures": { + "data": [ + { + "participants": { + "data": [ + true + ] + }, + "proofData": { + "data": "0x08000000b5dc030027f7000000000000f8fbfd071f6bff60000000008efbff716dffff1279ffff06beee7a54bc627e6a9f012928365042084ba42e63a1e592430f3fa42caff1fc11a86caa13c6911c39d88fcb4e1c1c156e99cbd0019a08ee7baae9bb5b479f3679fccde22034d2810064d03c077671442f52a1a56e7f17fa0d5cea782061014230e5a7ef4627c01a286667ef36f9a66032d91c554c1e1ced220a5f0234ed8820507b1fab5d529d0243f8a22e560d19342de3e8d761f9e863485777ae59f11e8f4df79e8b7042c3d53ae984985f8e99301cc7b18e08805a255eb28d3505fed5cc6580098a53f2172a4be50b9d18f9e3080e43334762e5395b741ccae7189e58a37a743c4a664f3d014ff699997044002710c78b8a6ee92f1d2bd62a3f13216f96572109e76b6b6fd126d7981d0267f21b1f2ff2895d604b2b0bc290d12c47ff960008fa411ccd7519145f11883581689776e039f10668dd046d7f43fd2943c3f40a5bb70026bbd3f7483b21853c316d871093fa9a494456395108af751621f939567312982be4210345f7afbf035112d022aa15f2253bbedb51e07d01636b96b51538999b672db81d605d2bef65bd9fdd39c92da06880cdfa1f5448ab4bb292921a1958bc59d8fa031f5e848036e11ca5401c5f86727355a97c93d5f67edd7f37141ff9571cb3df7805a5829b6ca7977067a9069f4f7aa391471aa5ec5a48b8727624b5e824cdb2d61bcdd7247272ea765560bf9d4410ade2784ed5c111825b812f737bca2edbf2575039be680ea6d5943a27d2870f5a0658288e8490118e045e4f052fdd2dbaa3e46bb971cb2fd7eae27b5e7c41370cb05652640db71e48f4d25ef580792184f91b2489d1bb60ffb89d11100b875e5dfaaf460777580ebf8c94374ee15f74bcdc6673fcb9b80443013f3d8c8fd12b67a7271f26d94c7cb577b87e6aa46809a01516112194bb00163f4729aeb7e5687b6cae58d5c9ad0c8e6e16228807f85de0c1a3745ed99636b276de0ff9647b575dddd94beaf2e822f343d3224bdbe84652c6b80feeaf01115044e51568e73f59b6a44b360e596d22752e552945ce1605d1c7ca733405d62f87f0df05e3a0f24827b984194c28632ba1f6dc3974f0b417a07da92b86389d1a98a3a331b20b332f0df08c751ee955312f93855e17dcc224879b6d3a3a01c5535fdbb819b2971400a31c7a70c59331120c6a944ba7f54c70d6b4806ff574343fcaeedb2edd335242c32e077da0bb774e01cec607feea02718d2cab537b8c356ae12c1c230670c7789f076a6d43edbe794a53856f895e78689e21d92aa935fd36666ece6faaa3ce1dc1154920efcea81f9b22ed142383cb7306a5f2647ffc5e188d97a7108d1c7a29af79084e52709e679a148632d942692f9d3f2228b6e72428db39c1318007677b43ec7a401d851d227a8f4d6e9460475dbb2e6b5ffaa25e2da258c75a20dff275c297b53a19c8d40bb564902487a37045cd7e4b1147b272095d815c577e44f34846918e7884762c71040cb4725a5b962f9fe9e476a8842410278f157da206b5152868eb02c70da02c9616cc4335d95138516e0e76b8124d73b94d996fc504de632131184a120e494e5e3c8f3ef27c802c002b7a16cca2c930e055954a6295505d9975cb597e6e1423119b6a01fb86fb3fc7c40a4d7a7de32cf1e4b67a7837bb4903a9475cf9e3905dce03b73ef56aa556cbab8572c6e46c769e05647bfcda39399e8cb97398f75f06ce18413456ee8b42877f8a451f277f039665300bf6b4582919f1ba2ff76e6d5624e13a0e6d6f784248756145f1131b27104ef819fdde687bcbca202e7c3dcc06d9178a1c167c8871b4e3486e50cd4613ca23b629d48e8a50cec8384241dec611c5fb2065ba17ca733782ac20e9a99b27dbe2c423ffb1f21e7df249191765b203abfeef0befc3224d2386193f17ed232c59749a6df3c2040f3513a900e03f180e93177c324772753adf99546b1fc31e3f9c3177312bd7c246994b861888543f248c7fbe497007a61bfb2ace3ea5f6f170fdfb52242c77a51714c928272edc7b27a781824eb5b74e41cf70410affd9802df1f9a80b5f429a385886ba3bdca4d24166eb33375dbcf732adf9400c57036d78babbd71b5e7acb4d4f9afe13229a5136a7964275ddb8f307110fd40efa2f1a132198a64af2213954a1a5487ddf9ead68a2932d54b19d223cf698aa0014b52b25b61e2d60a5949e56734bf115dc290d3eada2e15f28919f605516127bc0aba052c6a5f05d346d12698c6228617d69854e3b886053744fbe0c5d20b35fbac0704d6ce8026fdf0ab5572526e516491a8558a7c54a6c758f2f736b39ab4b8c56500b036fb332e878ca3da42ba42ce8ac1e75e19ce96d48e2702486a3ae2d78ee6e6c72df5955fa0e615bfcd9bf7eb23e36249434a85244e2364e9d093d40298ef41bcb90875d048dda2c657766567152d72a36c1f3070c4b5a06614be1593f6783663d467e67e9d399680a5d5c4be6178034a1a06360be2a37297f6beb7c257b132f4a4e490f6d7a8a537bd6b0642639dc6c2c43ce629eda371da78a591de4e0e64346a9c46bb618fa57f7ace657f5088c574015fa7536e51045dadeb338b86dc675705452444e32531ead96be2cda3b7927aec9bb6d71c722346891fb426185a94ecf3b193ad59806710abd1a36a91db577536c665ee7ade12d9831ed2aaac61a58eb18d8344ff95b0d0e63085f94c52204e228536814503f550eea266c8e16231207d7966a9d82b15d7b37d602098f1458ec9abc737801957cbcfea52e821cce3956b36e07b122100dbb0cbc7c6a502041fec22068b14f66231840394aaefbc93a72aa894f87bd7045cd9ec0291a779327860c4971aea33667cdeb324c86ee73070e629271e31b040126286d5e619fa625e9068376bdb276438590f12053b8b3716644a821d1616f6b2ad9b651014c1d691155a90cb6a7281dfac5e63a0f5c7f6d11b19d463284524c307e176982644074b48991593af9ab09ea41220ce982d8279885e1422fad7346d9da6a397a200603fe9c13304b6dcb47c2092c23772a6655a9ad521ee23d8b77044ccb26d95def4c7e95d816e6310b2a1d76970125190213e16f4400981a053cf68f9a7d065aaa616de77117dabca62cfff3dd0601f8cd7e7cfcfd27f1f714069fc24c0522b90c55e61c90774f8ab85fa2cf5516b151e809615534541dd98e3a73eeb22498d51b32738182632da3b5784c523e5c7b08ad3558cedb6b3bb8341bb664d2456b73d03d7a1da13de59b3516b7de2216a255595791d4d251594e3c6cab27d605437d0d11bd644577385cc651c5716c7b2439cc6ecddfac1381f73c79b276aa4f517f724737181600578a746abdbc865ec6c70824ff604158117f5357a6d87c6f85cdf71d9cc5ac7e0f480c348b233a4228e9132a6ae14e694887a60a45d2782b1d7a0b3c6c283c20e1dc123441d430770d1a4443b08d9331f5ae833943e42b4d0bd75816a0274d08c56a696a29876b48c330d8793fb9673e459edd68cec3f66f51d881752e902645b583e91e243e1f4c7cf6186773160a51e8c3f86869234970faaa0077f00d682ef4944070e3ac6973406b8a6d0c25ba3d7bb7ee092b5e6162c4bf90215bdbac13e2fbea410537841612f77d6cdab7f25d0b7fc074ae6b9f28ecac422566f77c1c76a8c612d5e8a60052c599556e4bbf5b34e6244dfec8613a4da523022c612d10248ce12ed7e4383170b693068275fd6b7c646954ebc5c24a2521191df715592198eecc4268f247049b725d748679555806be411b7976cd29280e610ff973b240cb240f0376852c093d07af7300066b60199a6e1884577c2f258fdd05594d1d739aa7e15ede8c313324a8bc2a78ba3a276895e501134bcd5c0442a64835a1c44c1209345e66583a04bc0e5b1396a6c201af6b2b7a1601b972e1ffde6147a67c1e02b843042c08b60952e549212e615c6316663167de64f62bc1b38e7780a0d91b245e27706b136d27e2851e2284c2ac211560fd09257a74760eef58056347802af555cd6a3dc0971b7bd6d9632d912044ee0845303ca8771fd88b9c3efd71e71080fb4403bf83814a79c23e604314814c67b7912933c72b5746584958e839bd07fcb5fa7bbab5236f8bbf3004e8ce18175a7f705e02d1284617be727374815e4c9690b45fd2468214fe93ef3b43d7946ed40f5a1cd23344738fa15f0661480c12f1284157f5f7545f02d1b04aa1bc203a7421175ae7e7f73f00cc76017e43aa3f9c307a43e74dc4788fa4c86c0d369f54b0996076c10c2c50f201e07ca1cb6219acf9a916dd55d44a5e29ae263dcd7b4455bc1d7ed46ab7477b3af4463023257401057a7857ff597ac2d51b5793f7247527ba2b12b9148f59e4369a093a463c4241732c52a9e1e243e6728a67391c940d9c72331b3b062e6d5b6ec216478f58325970bf0d6b4ac24048f6a16c2fd1e95d66c3195d7a35fb1fe8db9a414d96f8666f09c353527a5033fd633c3c6a708926c011694d58708747cfefa1742d212a47c358d352d626d35f13299c067420f02a9562736684bd6b36876ff058dfdc626a2aabf0442c92a74f7e71eb78edca2a625254c9018fd4c02e468770319b7c077a06c132737f7b722d52726d408929ec3ae88d9060c32e71078000a534b9f3b532ce67b061eaf6b8155e240f139fbbf87711e8da0e364a940414bf3c7ed1007a7d5134bf2339657e50cb7cf65abb55cc5ca0bb8847100a375b9c26a9186febff00ccd3924ce3d0fc4fc20d520504271d2b5fc25a00bf3ee73f5fff5c3fe2128240225975233e1da8522f427a3040560e1d386875083e46e41e01a9ed7c0d28de2e99420f5b4f26f762fa3f8c1e8c541626c353247c6481cd04b05c9515c1a20f33361c064110d0ee16252a857580269e51d0631e0eea96f05dcfe41860f2bdc57687c090315a3cde4874c93743068d5862660bf929705ba902ffcf7c5c9ab37a232c113c776302174beef5892a96c2290a7d33d209965c5213ed5c1f352ccbd24eabd45150a01e7e07984daf338f8308253f0c494c5b6c9a0889a6bb06c415eb1bd0d29e0e870167185bdc6f7c711fdd0b2b2c913c3560e133c8fca61ad3728f6ff59eb8462282fe3ac8ee3e2f84a905037582b81c9dd7dd05ff814a7697e25f312543c87bf95ba507457e757894310f0e89f67f728d6a94179f68f07969f12c0d929e4d1d1e73c0689f92d130f9000908fd3b3d28ed1cb11ff2613172265ce77471eb602184a552466961af01324bdf4effe23b1089b71f7a4467fb236af920716b162d79706c8026662ac41c790d704d20e674441645460ce66df1095ced157640cc26627300c8118098760191c89c57c64716152fb1ab70ec6a7670dcc08e3f8eb3941df2c3213f5a9e592ad34877220b7c884da04e1d52321d7b40b5c2b210cddc121a2a9bc90ae45e546e24c5cb55eaf33c7264c931202ea02238cb1e7e7ec58b8d52cc21557800bf8f4afdf32a5bd38230520f7d3214414a196a631d845fc8d46872e2400505043aa433deef403839508b5b7c39af1efea58d4feb93de1c86f8b90db2069173bec5222b5d220f19746b2c4ec1ad8549a7e49c34412e6e0cde51d325c4d9d967eeadf72c70977d7739a7a2025e37d6386c00515958870054c5912a575813db02d365e04b5fbf693dce51f217a61ae7415898041138059971a75af864bd79bf39442eb801aa60994f84988b7a834b24014dafe31bb77eea6853d5cf5cbc6a9606584db128704de52a07f21d491a9c5b4f44d780179f091d6fc9b347722b183713e2ed925248231931d1b3a649f98c7f2df7c9c21ca9687d4c2214e43af2715c218f3b4370dde15232d1b9d90321e0725b1d97ef2c76cbcd52c907102815b298617d171433e9a83614f04bc03d197fca1469902b1fe4bb9b4651edc04d5678d5109fafa7288f05be345b6b0821eefa0d21778187016da75f6760710802101f5a7a8d88333ea3c7f113e5cf765f1a54f208e03d5909bb55632796f3377676e7d426ba09196db873302e3d1cff06ea5d0f0024f403638616583fd688c5177a91f156a0ff7761062d157007175e273f4fde4294865a103d935a6bf122c37c732b64161eb2ca3dd237ce7399fef540d2497a09703d8c6a0d08a54a53d16d35802b5571cfeb5a0f4fa7ca0fe2286e7b3c4a38734ac3d630db82135a6f997423d696e364f1016348f454ba2ca450d514ea911d0c2e18c71c9acac151e77d13105a728f7cec6e923b9d8fe33d70e32670f52be7555e40ef0211efa04f6ea2921a3f9edc618ab0f80ea9dd3e3123c2cb345c6ecf177af5b33e15109c0749200d0c9ae45113c174fa04df18bb7555b3e572704d693586e7597ba418ae59354b773afce0083974f5671e1935335142a8c211bcb29e393972b9625ee2571daadc5651844e8b10afa6451bab28a925d1005b4ac6b6097ad4d0d05b19cc605e4c0c9d22117fab3017929e61129e0d764469ea4fd0b1c327aaf08016970f017e5a63e31a8690cd43039555044806654d93072c5eb8a25c708800a930d3afe2374a1d2f66ef796404b77168122fdb6a643bf9d00f3cb61f03db355259dadcef08ac5c33551d8b1d1d1af2351fdc85b307c2fa6c5703ec932421d56f7b46ba5c723fa1183e479e5329de3c5d26ff747a70488f0112bfc32236a1bb3c6a44ade73ed66ee75c0146c64241b3e72a2ccb73311895b36fc1c3e50dd3316b158d3abd093cb2b902869f8f5cdfa77c6568667656e0235131bd3254547df88f782163e835a495d46176ca3f258516e1214cb9e418699ceb6b7d857815d6385e508943dc688145f91c96cf2d17ac3c85704923b6286a4b15449db6c6183ebb44104cb8b370ea18c85a44fc0f58b866ab423b80581e296f5b30d972f45d63919d2d5f8c6b6ba473c030d7cac929a6586c305da0cb6d8c2d704aab052a12fea62e73b1fd097337a3e27371899719df1eb80b43799f617d9b55216172b44a568297767928311c8e79a65076e12120fcaa55793f782a19b716a3738fa579107d6df30690699879d72e546ec8058916fabbba09958dec1028ea5d29ea007848dd89c576977a490c61f9a43786f07a41b391e2286483607ab8520664ac17c53ecd511f3fe8cbf72f0906d1056d394e09dc5c5507c1191d6d67d4291674f0ba129dd60b120c54123edc97b662d0c1007bd0404d179f15161070907e37aa89682f8a812763058a43514fdb8b022385ca3e13b1ac293e15be73921a873795888a1b9efdef44b3c6d00035b768452d9f246ff679c558fb57ed1190f9af54eff92b0ba93c443cd4534a6c2bd99a3160c2a34abda8de5bdb95ba6cccfcfc6bae768d218493354904cd782da569f679698315546d231a0c5e311868adfe1c7c0036a91d67618e0cca7367644b83a31a5507bb7c934de3614bc74a498310dc3e3f192657fd210d5071ede518a468d916dc07c51fe6f65c47c1dd9b7abdbd4d538d9af75862169d2e510cb906b8f2233d222a1d230a780a5dc413ce1618731f1f6786420b2e330306fab764311a97116cca566067bf55253f2766502849542055511d47361c53274314b4f852f722a82b35b01b39106587509131143da243535c716a6e3a3becc609efc8d52edd8a5e43cb3290588e1d0d456b5ecb396d919d084b8f2f12f37fe7368579c94cb5debf461952e31d54b1aa1991c7c1617060e702d360ef24fae2df13841cd3475eeb1a7931eeef022646e134f349593a02ddda15deccf110436583054fabd65679352a169e03c92564014743ed3eaf72218f620358d6e022ae5b577449bbe742a0a5b15c049ffc25dea1e765a9f6071198faff343850716563c3cc6322731f2d67dd5830fbdbea1bb17ad41938befb1568668d07ad2fbf0793e0fd44861e7165be50e50e5e46a157a066623a8050af049ba0853248248209513d6c3611951938f7ea196d6dbd9539a58ccc557a92e222aca31951d7e218673400c543b603d1666ba269208f16771c30bac30706118b72bdb8f92a6b2c91736234126c1d777f29100b5c197ab14117e3dcae5f1e0b5a32af24845c90109e147cb323580079c511dfce21772632e4123413755e0c55916090937c07fd4c3f10b19f8b7698ad7e4d717d7d4020a85f518a37b137d253303226b0d47bb39b495a395a423160a02947e0e1594eef943e7264cda76c275ac36561c0593d9305f74072da3d372335da1bafc2c63894128a07088a4f7ef27d004a8d6f604f796ce40e0a6b2a29caa6d279fa9c6455ae5b3a5ef2f5440ae09dde000886c279f8f4f52b3f470c5c0dee863917f79d4b37be2a719917464861f81968d207fa2b4e275840e591ed1579d1ad02238c054547e30e561846807b0e517b29b645ed41f9bcdd3985b0d153f677df3f529f924a559b3a51a55cbf2c1713ae0ba38c7a797fa7b00bb70b133bb1487e7ddb346349bc91be4805e42d1333362707d383c471155ca323fafa00502eae78364b90bb040b95bc47b8a85915062a8031ba5b9a15cc360d5835d6860fda399f096bc4d97497535128a29f356bce190c71fae12249b7a6681aa44fde597a773e0e8747252aa7c71122ea21752b68b085460f2e4057d34bff7d6cbda90e82f440118f58a27d2c7e003771c236073a1f384fe9044f0572d4eb2d7da6425d61159b5f2575fb60dff3e044c64fc56ea6d9032268c9d672f06ebe41d7201e0828459973ffb20677ab1d575b9ac59e498e4cc876bc45db36dd30416b0688577c75569444db1fd852770215112b12a87cea3eaf6d5bb89073ca964f795a020f14f63849549c59d41de58d831b937a1f74f08052489b3512603418645675dfd42bd2d8036eafc1194f493aef7927e18968bc87951274ee771c9d605d6fbb7018704da329491241a5160056a055a843ff71d375314751075f3b65eb6622cd99a9678b8d3e2f7062540d09a69f3746e5ac120233d24e73bb6b1d1564034a3f06e31e262e0171eefa7c5938eccd4149c0cf0b32bf4a5c6c79ed4150cb3f43adbe17683c07eb5b16d65a6e50a4ad520426ff721d190e469d2a177da5f9711153a208782f68d508fbf9b6721c26b37d641cea210d860a4d2a835b48904e88003c05f65df861f03c9018b96242d07a4848d25e3bd3aed1408f202934299d1175fb7b8018b8a47449ce9c7e38c9707c7496afc516a89ab67e7fbd1970870ba869faf289308ce65c3d9e2ea54379a8504c4ba1475e2f0ac41db6dae44a41218776705f597e1a3b661af60e3f6548823d05d8edb346bdd81b7d6aa2d7267f436f01050bd370b139466746c85f6d5396c07d8f61dd32be3ff45b61ee1d3165411332b9d7c36e8681814d661d1a162794f9072a33af70e3f9b07d135bf113b4307b53b9ea63439616a3733f9a36051d783d76357c2c2ecfb98f72c7e160514e15507b8fefad24b87e7c0731d3f7402fa9435b1f081d5e6248a36afd26626506aae108e09da352c8e5f34655ea1a62ce19e441c3839339361dbc04522dd95985a1417898827d6621feba05dabd1f67daee900b7d4fb7034777f620b6bb592f9ce6252ad053eb69b0fe94531f840e4075556259328dbe48ba17972d4623dd68a802067066c09613557a9038980af3256c449e7076c488046bb22814532943528f28cd2082c3713630e6fc29259fd5631bcfb71c2893481bf22b1145aa9be6786479315cc3879d47ae882b70d15fbe1b5f48a3096cba2c3ad5be6c001d88cc74a5bfaf1da2648e41a05a167bc45cf16e4f8def0751d8ab4f0b448f2a03dfa3085f235c5bd0ac375969a399268b1186042381a857824ce11b24e12d3745dbf417fe404477e69fde58f646057305a707224136b7510c65405673aed2769bb62c3c20b9a17aa592df21e8072567685d0e30d53152591fe6e059977d8c0f2810947a05d83635f548cb6b9aebf650dc7d2244c801487c8c02c42dc54a5f643e1258059dfa94188c4bc53f993bb444d1a0ad5a1b4b483c1b8d3c37a04afb2f8d770467bc2bb15988875f359f430554d1078e04bb5d4957a942cb3598b9507cd25699675f42af2396fa4570c65f9064b708f47c01f9f85fff5bc762caf90663f1918e656683c10d139ba51a5655c014926c7f687f3cd428f5305b2abd854127660a044d5c65b3121550c10b897a8b6802f962368567ad3a315b5d780951304f0ad5937bec873679b21e6470215bd34866796260ce5cbc02c9dc3d221636325553811a7da0041f4067d84b64a466873ea5ad544000cd8522ae3d570c2de53541bcd48b6bf0bb0c2052891f7790f70e2ab844ac73684c504695bdc35aaedd227e80ccdc05a13622357f450073608e262dfdc8a2051e3c202bf1e8886c05803e3502ca1478e28b964aece1cf2d60b3f339ab3fd8096e1301620a730a6be62dc91221ad400f917ce722f2c9275402ba7e53cd73b74e814c2e71c9f9267a2096ab7aca7fea6708a62074be8255204560df3d566a240c156c087c0579306066a55171d08b9d3c9283e7262bdc287a1d6c722a7106833f2a36e544c025ba4448dc6a2a90862e2391e7e36413cc892b7619b94800910c2efeae7f6b435ac7783688f12d827ea9070d708230e329c873a19590142c17120a391cff5cceefd35ac55ab4758194c418337f4c26ee749800ccdb5122a938d86efead8a45e631fa183414fa4893dc7a137807d65e3ed7af55eb7efd0f7f27dd41b0ef4614c42a390c179120234fcdce501182f25bc13da068a99fca57c709906351628403ca1bc074119f7b5a833242408723282c7e58fd3d534d8605fb324c52c0321550269ea17b8d882b15f259293389af5a72bb2abc4d2d3fdf150a631f6508d34d0a0be710319e73a51f905c905aa863aa609f919f6a9b616c069c3ffc17bec4fc7103fe353f0194307cf1c14f67ac136b3470bf164d0e99ac0b6e0ee529ee38c62c6814266bd570d65caf5ab62341a60b4cbd09535832338c0278bd905a2e64722445b89b47f737641b45b7bd79f622a65694e09374aef6aa1224f3293686279553ed16210db111b6163a9953765c052b493785b71d3e47ce6d76dc4302d2f4ed1ed35af23c90e5064ebdd9372486e19c2e7b696857272461154802805bc0e1dc733c17b46665d2f736d7166b592cd6d444b778c64cf721d644bd1ea71b044f187192745802654af42a15bfb63c003f34233bb0ae60fa46c822f3f9c0621fbd3b352385b0681e273c01df8b8e558ce8ab457f15c94bf02c496e45e5b8726a7010252b955406f814e90ff14cdd297dd377458f03ac0fa0266b217de6a04984ff0b12d0df1e62b94e896414b631569148a62a0426013b39710a3d5cb2f2024ffbc344773a306b99b9e24b3e47f32b841d372181b0ff50d768d64a1e37c15b0e21746692e55b2af2350b45af19ef069a40e91ff4e7a755c183ef6cd9a1612eb011a30a42169a75faab681f5eb4707ceaa0913fd5e4cd36841f6a05fec8ed2c5b73be6952f3d4397740f645899b0f49b919bc4ef460077566b05a2415b98502572fe12f6c27043775cd2d66e6282871212ce3490882c97320757933bc96710bdf948e4b9b43006744f35f57c0dba077fbdeab410c4a596807902f0b9db088118dec1a21f005236b3de9d055258af92fe4705c232b53c0627da85a16a65e9508dd9231052121607c41af5677275d795061d02746fde70e04f010aa5146b7095400a1be31a3bcd22415c237529f79db6cb5e0341640486a0dc474113603a2c767d2cf834bbab30008fac23b290e6c73735f87937bd2b6840df543460d00090536ab934e72249ee23dcb6f2550dafd8a5371c242045ab07906664e983ae7c5d44b90527f0e8b21da32523ed17b3886ba3641ba3403d859ab6ef642215b3a13b556402bed5ca57e043834acd31576110576a23cfc17866f1b12a092b320c1407f58810d8922bb81bd4481f4270b1e103209c42a267aa0b2353eb414d7444e15ad03eafc6727622c1c458ab0e8030836936b394e355b82d8615d403c7b6dc8d1f40374a194329eef6e1d3421b4074dbf88128cbce8067d34b12b6bc42e14ddfeaa38887f612cbab94d4a8ccea943c35da2016e14000762757c5e8fd87f2dffea7555296bf119436cc61b9cbb0b225780e128d192556e3016331c575d154d27dbab137624184a040d1a32b90eac6410adda35e438bc1d5c6efa424abd7b520b44a5566e671873284f1e54e65f08728697fc6e3c76aa425ba00f43022db0332a786b64e67a7d7b5f60654244c27f705e786310481f200ef0c9c4330ea009393b22837b4e074e4142a58b5ff6a1f576ceaec92ebb476458e84b201567d0ef0637b5796c1ec2db70bdad9b69fdf3c5793ce6890274717a79ba86c35bbeae327481d1cd3141088874d4cdd468087b8817bb7bc4160a78c469ac5ffc59a0959b110f4d6f26f035d12c440d6528bcc89863d899467e76361544d4b54c531b06460a7c1b195f79e8693c8831d6354990be6dce87fd5cfd846f75d6991b1d6e634c281b5c74614211860d9e98fc6bbfd22035ccff4a79c2d0f80a44f8c77cb320114534bb194eb50fff79d78a6f107837177885d52e27d1e53a3c5ccf3f6e0cb39f0dcd0e1059a5795128f6c7e30e2bd27a34dae7725efb29966d0ccf551633246f5e6208356a944afc53b853ab1ad77c457a396ea338cf2c7f2ed054e21e711efe491b410130c001fa74052acf1026bcb65dc4682a60f4bd4f4134050a579a7afc6a505a9c279806391cbfd59740f44a5202094c4f3058a516717b1c617aa8544c23cbe5187c92d0c3383bab22718d547155b0c6e9077937131816b57b640b55a873b08384206d045a258be0ba5afbb5414c2089905e223a5b4641e5623ff0fa537bbe39df4fc1cd6e5d7df1cf36f30a270b214f281bf79eb5083d89f0458763df426dce1874d25560260f8d6840d41b764d4ab42e6d49f76a43ede19d38e768bf2fb2c90c2d0e614b69640a787dbdf7dd0036f98b5711c66b19ce51d7312da403078b215b62dd789e5572f9c85add554f4002e661680e760653c58d561ce7e48374d914e3113662327bcc84f353c663b12633672520b672cd7a16cdd3489ee78631ed0f521963634058ae8b3404feb2ec09f824c478a12ea5505d9fd477c865e83772a4f900edcb1c72dc577859872668777fe9a10d5722be0ac6572414488ea6274bb02e773f6d754b4ecfc67cd34de156b73b0c2b6742cc28224cb32c7dec7a01090877290b0e1b762a2e411be2ed0d2378dbf13a035e41307076604fe619ea6511f3933d6538ba6d8821af419c923c4c9ab4fe33a97fef2d3513752c36dbb74a6a20292a3fb5ce60ca55aa73343b373afa66796f03103d4316cefb37c4f6451997f7b20229a86e1a4caaa827ec68775c66c495350262b3083f56523b3b8fb32e823cab08af2a54729859301ecb55e04786637a2170d9890d01a3a7022266954e2a428a7c05efc467b9480c66502b0d21d3d3a007522eba43b48292366b088410eea978329fefac234743d20185fbd9142d21ca25242de85c541b91463dfd764ed004490751517d34dc05de0dafdd3f65c079f44bc3092a16dad4692f57bcc0407905901b0768ef753e00582e55f807147418163a93fff75a5a807c50945cb6310b9b6c041d887511327d582529a6b8793db93e1664ee9c1f0238b74a2f1e8d4ed708353541f823704cd2503e6395826c6930654b2851ac114aa58105c4d7ad20d272ac7849a6984d896fa0199fb1584718cf606087c14e7b15a0723ffa97672dcc02602e2fa62b5bca4a7647c6eb3f1ecac56c05affe44770b3a9624b8a8004b28c1cb5c4c242d2eece9f3292a074c7387ea840dcad29c1befe0744bd4b83a6f0755dc32b2c2491a9c23957416ee3e247a96315822c83f78f94b0e0cbedaa751443e4f0ece14551a5770cc1c0fc49225e034ec355f9cd36c2bfe8e568688f403c72e09751db1e029cfa659710f74e9779fa0f4567e365576ff201d6bcb7d010ff9ec9d0d0210d94afc7d7164d2b60f723420fc0b0e119642c1643c5e98f6fb75ec511630bf46d2628d35ce7e44c86761a7d44434b57294086fb4ef1469acfc2b1b0fd14f58ee0968795bd627d1218072532bb041fb18d71293aceb140c01ac188c01ed61aad75e09c6eee820e4a22943bf3cf947cfdafc42cb1d301d1186ab640181aa68dbb4357211bfd76e0c2183357b99c5251779e42fd6c0af1628daee2dd2b01a429a082633c9ee1c16f295e75a433cca63f6dea15f3bf1c63072abce6eeecb7860af2f8412260a9446543d1030892dbc64092c0b74c0e3cd750923362c0438fc6fdfbfbe0df173431850b1e11171b6eb141f1fc20e4e4e0b69a38012294caf706b9119ab3fc321a6401704c6029726f316b92f6124d726641b8bec554f7278ae24ccd1cf54a7b0eb46d355d5461bd42a4784d9572a0fd8a2359af316610de89c10030ed72c6e5d3629bca7c90c5f96f44f3072d06cc89cba6bd576bc141a8a6652bf375a216c25e210a656851f0ba96115063fa276c7ad12183e85734251a89b550a649112cf133e101257e76604dbc37ad3de1b3e28a9b923b66cb102dc3e432b2f36cb3e68cf0155f2b6b04859c78b50ab7af514383fae47250c813730aed86a432308192c44f43f027edb5149713d76a9deab7ae182c170a5852c43fae2b8665eeb2e7d32ce5c3c600ce92738dffb009df7954d055acd1a6e07d22f147e39708e19bf1080b27845f03eb47527856a5a01c7c7702d48b91fa8973d00c1c86f0f76725a57dce690082a03ce641412d630bcca703f41f681244996866eb085fa261c64d714b544dc278b9d227719282a0cd797900095b20a438c2bc00a0584b4738affca130edaae4f6fa92e108f2564115f673554d041cb66c6447805821df46f8ff6531100870963b2f607168b3b110c2b5dac2a67a372367ea3180129f0ca79e4b9a95180270358d9a5856fdb1bae2ed6ad0571654d52648c36c350bcffc17510859f00a583093809f9081f271f244562f0c5255915d35147e7494133cc251b5f1fe94eea47540f08fbb355298b5b47163e8a0fa9f72f1bf13ea14ae5e66918a77ad70102e6281f9429e054e4fbaa02e276e97cf395191c7dd960193d4e364d7fd00f7701491141e0ae66729ff32b7ee6e9857135e4634abbd937682c6b256a7791263091f1e6566ef79813b5cfa125f4af8f6e9f239f5005a77b603d77671c4475ad18150114223685e60e5a4317495c338b437e81214604e27662e4a2475e0181914655dc0267df14ff6590b8914099c7323e69e001683be6df2cb1c93a649d9bcd63f0fd1a44773bf32968506c57eeb80c2ea8af1220e4b0763c63eaa91cb8ecea65f1cb866a68c449698ac1e84125e7a407bd0e583923146e4d4699b633f3160058e5efa379a109ab3ae14a0d7a940970500aa7fa2d74c5fb63c8060a6619b71f5aea1de9734799fe459655b8646531f803cacc2359a5c5716a644b423b50125758d50db84dc9a8473974c74c43e94d9f53903e360a7d006f4d74381e0b862ae50385970073c4c7965016b7495152ff665fd7fcc54426941f347ee8f32c31d3b811849fa35895f3222a835c2b6140ff1417b7d1415dc91b3b145fe7f57b9ba5762009a70d71db237a3d1bbe0c7a3a0aa61f94ca7a2052e7b72d31ecf81d6e24af14071b024ff02db47b9fd1e51f2ed2604250c57d734240440171cdea7dac7886423b07c66929180b3243378a7bafb64a3019cf057ac9a1122e8fbd90797b08555a7bffb04ffcbbf978e94df03bf0fd965fee8d1364f2c785137bb8f60ba50e2b491c40b3037d057b059fce8d4fba9cfb542366725442ca764e1bd0107c6938713a2196c50cca82b3453166df4e751c71232defaa1350f84b0505876453a104dc391130951fe83c9f48da69ab637afff238062203650f5f9c6d4e34ed2a744809015825b76598c49a662af2477396913d43e103d213d77b5469e41e044045217f5bf3bcc67ab0d74944f6f8ec5fc9955b42de4a21363845ca79cb284e0ea700000012cd7c75f4518b1792b1964949c7c114012c311c27ecd435709c207b8ae6ab6a1456365486bab81bc1583537ea367c2e9c595a272344be28ff31653d6b763d6658361c77ecece130630aa82650411d6ca30b1353db1c77033b5537553b7cbc578a62ce2aab0895330bc5b9408de16f5603dab75fec21ba0bc70e5814773b4a622bc4820425a097020873914c9793bb56d7f6fd0c1f71fc020b5ac94424380b43c403f557ee9dbe06469e0d0e0e82c266c214057573de143e33c9cb36d9ef233542d8ea1cc02ebe0334873a6d778e3b3bd401fd7c13c02a11c0186f68e13e480a99cf913a9a311e376fa182039de7af479485a46c334e6a34216acf72b6c7680e098dd33b7afc6a237a2aac6bc5aa52576c1a93540fa1a04cee3f944c2fa9f73218d6ee528b6ed314d9ae2e149d3bb31143bc6d2d8592b6408fba8b4843ce283ced92d53f7a6d2e461c62ad72994075081b234a290e87bb299b47706ec0637237e10c713b7e61cf66df762b4995dadd799fefc2207661a215d0057b7ddae035400d200c2c4bb6884acd189f7340db3a420a51cf4e0dff1c7eb991320420ff051c44c6893d94cb6559f6527512f1de6c111f99812861c482666275e03c778c075c3078791c5e613146835fbe454c817d42d83ee71b6508d332ab61602310f9ba792f603f1df4257171139da13e9eccd31a1771ff0285957c74f7635b2c0de9f041291568754776602a3d744e0516e85d6c81cae95f141b04682b69113181b8035a1ee0f603b8fca30b0a2c343e6e272302a0c336287138ab14b4b2ee186afa2766b983ee3d8643571fcd91cb448d66ef2425be736002e68d24d4bd3a71ef4a24058288324222edc8799b1ca603d5e40a4aa65178402a960f1ba204196985198403fa68056f01945116b007c955af5ab9463d14044a43919038795e14708054394465341e41dbcc86403981fd0be8baf5104afb775902a07f14864cb646142633139d56d501fcd44721aeaaec1b22209e3e63b29d17be7abd34bb4d35352a0c6b1c2806f43a42457d59d9f44c5562a9ef58864d7e70027040591016fa4f97428123e320ec77dc7aa57c55727e1ee00a1b230626407339b551780c5960602142d1407ebeaf10b7e7677c33ed422ad9d4013c6b530b5cdb7fe038197bb3089b7bfd2e540fa815f5812b2091043116fc2af81631fb1354039f816a1573711eb5e9be5b53329010987ba40459445e710c462665028a0266228dcf7b4661ce572124ec495f2a4150bdcda46d2361573d6e80b03d4549c13d13b69b4654e9dd1d3b45fe16b240bd51a650ab5df5a13e05cfe0175a6632043b24bee9798512327beff9c8378845d66ab097b80eb1c2315163c5770a8b864f1c7a94c836cbb710585cdd2b49a312533977f6a46961c76c108bd23c086f07b745ab15661d84a0454cd30cfb1b1541a33b2a8d311f7d514d22282fc0486ace8936f064c1355f9d5a653c325800af4405291372372897820b6c3c6b5c5ba552047dcee62c0c721dc167fa41021bb0e64b4fd006bb601d61aa29fed5643adf8a4a798fb64f27e07bcc5b1089a16df349ff2076fcad7ef287a841a5cf6148e9e8d37476ba4666f6bbd720ec54645390ffc266d4281b60d854574e71881423e6199135701089429ac0782f3c94d05ecb25b477f3acc65b22f32c2937982d2a0dca111a28439d41f03afe665547a82f62d65367e4c39c60c481521726234d3a3c174c79c17afd44a534ef287148c54326a0a11e50f6050006ecf63711c3020d58fa2f5240d76f25005da407d054912984b50e40a8e8b843641c4b23cc1395234c5d74394cf1981f17d2336660ae1b08ab1090513385677153ea0d11914af72ccda3220b80ecbe3e21a26d7e8c50713e4bcd166507ad1f1900f81875bbda8233138ed5122f6522591e4c431f13a7857824f5f3614521c71c9d3f3b5567e72758ff9b1a7a4c7ba908d3e5a24f05a23c0bf972254f06fcc35857defa4347b46631ebe80a75f22755629e2f945ec57f7e7cddf35c7595231811b44cd313bd44fd584e61641c47794b30dca8a532b0072911d3626d0b286dcf608dd8aa4fdf9eca76b92b854084638a56861b5b15e73bb0409b1dd96d6967177137eea844d7fe1d4c8feefc6ba1ff437876b5086ccad88417c17e7e5fba83d3742a06c24389096f2defa36d70371f0d57cb89f51606bb7634ae927d356c47e91265171f649c2c2a09fb89b35ffd8f530c8b6514597bdad73e28812b6b83a1935d12e75310b0548d3de324ca79172a5a4a57e437671f10810818973762cfd7a4254114661aac38b77014585b4142e7851599ac0b5bec31536aaa5d157861bbc62dc465a303bd953d00de266279ba17ae6f8b7fd95f93014f2d5af8da2fa9d3452a7bbb82208fb4dd1a0448ba598cad965c3172100628917d278e37cb63d40864677784e52b54734c7a83b3e146d4624a692d92ae43478ad347f2227f72eabf41657732cd578ecd150620f6566d75dff916b062f3622f09fc462dc8900fee46bf5f4eb1ba4201beca228f6f0a418766c76cc5fb353570faba380137b000ce35bd45b288740ccdefa4539d597e661f36e00eaed2556b0e0a42132d78eb27479a56063b308353cd2c9a256ba3a15f182dee573d305e06d981040451b1831d63f4ce2a02b38a604a9b790f432ab23c95799875d5e6e108de253830c90cf61527d1cf3a6182463929bd53493a523a38aabffa132eed2c493fffe9303b85902a8419103da197702e6d1a9908dfca436382d71871c81bfc26e6c49b7405abb141a2abaf073c40504374b30135e27cdc7060428e64cb052c76f5c1973438a32c31ff015463e560d5165a30e9448b12384d1cab106eebe99326b37c6208a0b67c13fc0fe62ac01e1e3f5edf3c2e2ee76d54966e0a3ed27b2b48b9d22e45d080a27c5fc22d3f9d4bad0626401f4da728146e41d7a2495f2e164114ee497285174d64786dfa4c9c568b308d85904e893cc553b4f8d85439a7155e76622d1afe7b70733df6a81164a2f27bff75e2322992ee6bd28e070ee06d58383ecbce6ed40b826114718858f8470e4b61c09d732d07992a3054ea23cd3cc119fa3f241430a6c86edd765a4f84b94c418e89750f83aeac12ed602a1102c18c2184583734e3df93686e6e0e4f3fd90631f3d85e54a010c83f81478f7cafd1456f517d5031b1782a1c9049bf31c0c2e851073f32303d375c1a82f9683031ad337c58372379acea9c63e1d6e906a82a6d6944b3970d654a2e70f35d8849aa4e8d13a2459c2b8c946742b7cf981bf1d20d2cf34deb1ec15d0112eedead3137d43c2479f661226914bc2d785d2974307d045207476676316f86197809094388adb654b706d31083f0f02e4279c85b85152b63d835e53960835c43a85eb006018a3e3273cd4b5bcb04625363795b11f647e84d16f49d50251ed55665bd6051f0e29b47262dbc32f5c7dc5631d5313032c9de3d2429cf46a4f0ab059f0ba3694ec033074179851201478d06dc8af9327126d13cda0fd11dd4d0385bc2dc0d0ce243b100c5034d31a46378316d7d287bf5fea353d4cf7b555afe583c881dc40b8f30720df9c72a13b37eda5dcebe1b2f5ee33523a7edbb2970573a420adc7476cf8ff172b91c73205d03744aef240279daaeec695fa8ab51b1cec035857fd73ac102af1f86638672350e234935ed3867e1747d301c60bb303f6d7218000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cda98e6f594bef6f4dacf04a1ae53b6edb8364198bd0b71a8cfe4c2428bb245f38e69e6cea2be9739920c972c5d21a4010c2bf40e3590145ce27c56b1172636beece2717cb765635694967462b13851b2ef8956f4870c2386858943ccfb3d36f1388dd130e235d66435d7f33d207216d3cfdf0055be1a1300e235d66435d7f33d207216d3cfdf0055be1a1300e235d66435d7f33d207216d3cfdf0055be1a13003f2bf5af26b8d299bef801e49d57017ababd10b5ae254116c057c0855bbac1230e1fc12313cc076302ea96e15ee8f3acf278a03d4281f318008dd3b5d0f5a7bb62f495551a91851bcf2173cb2f4f22bdd19904c967df8773d70c60b81b9fd38a8ab5b569962d43004d1e72128b3ae1ffc35813444662661d6778969701d73079030421acf2de246274a001e7e97f855d73177000923a401dd225e0ca304504900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c3ce805434fea0f9d088e7a74b9616c8fc820725ae254116c057c0855bbac1230e1fc12313cc076302ea96e15ee8f3acf278a03d4281f318008dd3b5d0f5a7bb62f495551a91851bcf2173cb2f4f22bdd19904c967df8773d70c60b81b9fd38a8ab5b569962d43004d1e72128b3ae1ffc358134446626610300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d0000000000000000000000000000000081ace10790975b37c2e35c3ecc74681205455e6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b44447dd60fee63da8c8a2c6012767878441e08c348794cfd4e9a75ece66c4be542b43de95cc422bac8183fda239b359eb4da15418b6e5c51f3f540e6079a2a9a60f37d81e9b35babff3845e8919e33b1eeb013cff26528d784030c2a060f55aa5b364fac4e7760ea231b72b59a86426059da46bd1ada56ac4e7760ea231b72b59a86426059da46bd1ada56ac4e7760ea231b72b59a86426059da46bd1ada566371a6104b6094066a87e34bc9fa6652b9480b286ca2986c8f73151b2e5fc21ed517dc20d28b30415a1c8f4e8a38fd1232b17b7b733a2a3c4686da1e8f250f4bbdc3207e323581418901321d86e1e24b0266236dc0242b0f4528b716960e6c0460b2101b1d7447286b841019592f2e4c1f69da1508d9151688b9607aa676f76c7758b46a1f1bfb4ea46ef1270000000000000000000000000000000000000000df6ed203209c010010967d202dacfd32033b314c81867678d85fa4664f3a46638ebde952e2d3594cca717b38b92f1b5a0ef9ee075ff2d92b6a08937951556c4a4cd9f7613b0f42262afe6950d63d803449f9b47707c46a6f4134af406a41ff15ce2135409f76e9199c63b55bc7a0b044df4ec716db21ee720dffda1ff56c116d7a8538370d1de66bdd0cf30524a43260f3330e55c936160a3f15a168460cb27506b771146fa90b240c265a21d886ce0d23d8714400000000000000000000000000000000000000008f6eec135a60f2490d31571970a2b62254776133e9a2bc4bed6f230a4fdc8e22ff869a3b910f6d11cd39e679d90db918561321641cfebd5c8ea07f7d7165426ecca94a6bcc65144c09384b014a17b23920bdf6725bd0c8689ebb5b094eb2973e180e143014d64e015b8d9220f30e69290e40740fdc882d738270c3498707c80b2c237622449cf622b7229f372c663940e8d23f43ff7ecb7576d94404f279e70da73297246bfa3344e76fee677dcb3a0ad68bf208000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8de914f4e2299603b94b145d0536a3d55cc5f2607b93c6e50c7e40176c8ba138e963e461cf32f11408eac707fa053175b75af711da2e723efa5646a704f05661ebb2d6df93d253e63542b45ad8c0c7118ef620a0a9d15603ef5c847e5148d7ada4dcb7836299b371033800e11c684307da4674f58ea3233f6213713dd8bbf26415cb56997509c4e3a52f15a8558904257a03903da79a2194a23c175c813c90bd3491604f0bdbd66cdaf2945a3b4846a2e757a48d8de915f4e2299603b94b145d0536a3d55cc5f266a841a7e9cf665111226253b54f685777afeaf14ef8ef473b03aad2e375f5107338ec1426a9dd623b79bb34f34c80549daca82561c6e0d5b2eef4251826ec1500094a123a65b5d435a3c607810da471dce61bc07a018dd150605cd7e14d03552a5e59f3af72bcf1aa0aa0f715eb7574ec965d554ca740a6355481c3299e241208b336e63e372190ae6b891440c36390338975122bdec922f353e061577dada08f83dfd6049d216039a78cc5d91c9901383da680fffea62685b7cc22d54a1eb302aec8f25ff30ae37642a412d7e41c20cbe8ebb013362ab55067c176b534ab116f7537d537535ca356b087a488626c0003db6bb04f4d9735b4c56a83fa084985a32ad4d104d6984677cf9f652f421ca240c574820e5b1d64c42f22d0ff17f58746339773a5496eb10d7401a1e7f279e166ea637474708c55dd2a89845fc80987ee0aba00b269f97515c4dd32f0ce8bc3f799cd2022cf74776eb91c4674819cd494c8bdf5c9c1e6144e5650b392e599f1ec394072fedd8b54c521c913d727e996cefaacc004d03e25cb17c8d7161e13c510078361f349ff94278f60c7714893250300c8f2f53d55141419df86500010a16ef90f911f02b1d1e9c250c14bb2d7e32eb392559a15439188d54e53c76078c5cfc5d64629337de32f716cb039c525b0932249b359a4db41c06362f5708eff7774443357522f19c34f9885a7a7683ae0f92676b63ff80755d1bf74707eb91c4674819cd494c8bdf5c9c1e61442ce05d18d5f0a718b17afb19abbc1179e3eb581161f7f11fb164321a8f59a16711cd867a21afbb60ed28350c0c775138cb64d8697b0ca61bf4c06511efe5b63af7e978023377085f3e5713274286a2721d196e5313e6997b4d2b10726ecb6a095198810c3bf26606fd70497ed396aa01e297154bfb055144d408d764927dd07134ef415c2ebe9b2d7cfda11a3e298c72358bb428e5f77837a851052bbac365260bf74717eb91c4674819cd494c8bdf5c9c1e614451afef6a5e8dec6db6969917ba584523aae46c34d78d10351c3e425f1854c22c064dd95a265ae1106c745a33447af928f7cb7b0c6e70047791054d040b8fb4193ec3954d1c3b4e3696f31a4836e6c245ec1e8f014c269a06e5155e5eb83ad12a5311de3e9979886ab2f5eb4852b03312f1b4a535a5ed121ec605e33eb049891f1be48972d44d0003983d8d793d4dc5134e9a8524922e881aed6a47660c77b34e0300007d000000000000000000000000000000000300007d00000000000000000000000000000000d0b0792b63e09e052e72d10e5351fd437c092b4500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c43a6208fb0c0a7b2a0ead3ed3005b4b3a6f2b3218ab37602e76a14be2f27c7db8436248e532392f6d3c0637799b4c1e96019f2778ce6b3d7031f4493e56d85c53794c03b5761f4c117e8c49e2c1446a3e56d85c53794c03b5761f4c117e8c49e2c1446a3e56d85c53794c03b5761f4c117e8c49e2c1446a3e56d85c53794c03b5761f4c117e8c49e2c1446a3e56d85c53794c03b5761f4c117e8c49e2c1446a0300007d000000000000000000000000000000000300007d00000000000000000000000000000000ac4b211da0589f75319fdb4dc3a6bc7c3a76535b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c29f8c2da9aeb708a7e4ce71f363b5662f2d9d33c6fcb019070e417117256b04a62a44436e1cab5fabf0265eae04f034c914a75ca54ff17246e44a46370866335c90e026f4d04536d2d43312d2edcf1d370866335c90e026f4d04536d2d43312d2edcf1d370866335c90e026f4d04536d2d43312d2edcf1d370866335c90e026f4d04536d2d43312d2edcf1d370866335c90e026f4d04536d2d43312d2edcf1d41dc733ab76ec76a18752c1646a51576806b4232fd91bc37665f424f728c1726799a735741d750655899e332a578db07bc3ea96e3c4d5a7b8121c608f408d1261aea2c51986e7229df05ec0f72583d412ef8323ea3dccc4b9a75923d62b78a24d588784cda9d177db2e41e370cf0f563f5c70b4f2d1a090a6b35935e26e6b11b54754a2563061f771dc0816649395b6b052e07515822290b6007355fadd7767e8352e17075631927994eee53a732db188e18e622d153fc3cb0dd156727d69b307558dc4f52764f7c98b61a4d350dd025c170647be5915c4c93643c12d6ccac63f9636b566b90a208e81f5611d90c0f389fd9386ccf4344083cee9a56dcd23220fe2c36013e00140d3a1d831f8d76522fed54391ac831cc7b4d79a5606a501e4e03276357fdc38d1943a5a97b28812d3ef1e18b6ed417690f306b952f4fc24524de47c042fba99c6da7e2007cf1ba8b691e15a008fe0f177e82f5b609543d621cd6ab77257152767724978c73bf0e12732c653c16b8328971e1d058193ccbb36af8aa9c2e2e676465860947564bae531b3863a252efa5132ff977680550fc636a95a0007b68db69591f607f7da794e41e32e5ec62a74a615c12cefc7525a7815cb8e9ff56a4cc1f0d0d14402095b718026c0ead13b63d410ad21c3502ebad8d59c892be4efcb6cd65215eb636c9a38a11342600443c8b8a11ae579a6214096d0290ea2f46ff759a78dc09f314a452cd0169d8e34f802658511b56a535c99d9f25a9b81a1932a5550b3724cb5a85ba0c6761388704dc855726798b357ca3a67454b4b0cc57f0db3e02ce7a8114a3c4c86aed248f67b2ff990afcc79e1c139a574cf33e405a68c36e1048cfe85cce352c2647b14b4ff976dd1c5d4ab41929cc8c2cfa5f1a4510fc88174407f002edfa0853287294543aaa7e2eddf5b345635e0046e145bd07f33f80536dcbac438f444d31537db329dd73445ab6728d279006275c7c149479f0d19b32f013f01ec5c65350049c701a1cf4e3469fbf81525f05d169f3823e68bc14f517b61acf0b87e3fd4d2a7d4237e88439292149001f5efa414c5ac88e101524fc2bbd39dc3c5e21577641c195109e7f9f66f1ce7601476733373b95590fb1d1ba53a0067f705de524651aec7b1817014a65ac7aef25cae4960f8bc6a0296a8ba2381d88a54798411140f7c7233ca6ca996e83b8b34fec0c0e307454c33494588a1101f3d5493032d66a46feab3c14546e6fc008fe6a772f215ebec3ba2098c282792e30b54a6d44536cdcafb60b9b28a90539670566a8c260147e396c0bf9a526544626dc6a994a072877772239655ed678e80caa2b8f98da1f6603877393f132603645985afb55bc1dfcbaf04a866b5b446b8b161fd09a4369a3e0e47768d4fc0612feb212cf293a6eaeea9b718e19d417d19b0a5fcc0ab3618c36b963dbc349262c73f22040e8a21497b7f742e802987c8cac1b54b5c1777c19f7fa724e01867babf51b6c59ada81182ecc0376a19a8365e481e170f73b96e07d90d392834105359eda30b7fc1a8735ee57a366849c4682ea6ea047d165354a316055a89e4145aa58dee5a42c7152bc4051d3b324d7c439e1c5a122d2b0f7bb7e7510bf5663e15b13c03728b189c7e939b866bcdd1276abc69441dc2ca70053dc4bb0ad93daa316ef21a66ebb30a7b0ecef361d2d0c24c78e627447d4ab54efad80d096d7fef721b658b0e08e9465748fb074cf2bd5834df921f16c032765b0ee78c34ef8171704066d6416759415288ab7710d9962064c48e553ff3076b0315976a213b9f8c6c5459df7c9bf12a7e147a0f1197887921ea298d5e0fa5df7378e15c6d84068413c5f8736e4d5d9d5215497a41dfd4fa618efc163c705070790010460b7c82d663dee89d2b444ab544c40f66230b2d6725d6c9e92f8e2d745a6045d65829373b111b22af52f5c5c46c92a6190bd1e3e134eee7005e08bc8b1e639f8b4c9310c702ace0fc4fc921d524c3de7305958b755469935b637e518a1b3ab8205766f5fb7d622b1c448549724a6d50e6235e0b2e163596214dc5df9342d734681be144c41d3d7ee3303749c01dadde7163dac27827670d3a20872d554c9442ed7851eb7341794fd824f5e87e6883ebdc567223045ee43ba6113a9be919f43dc55e6f5d8c50ff6be361bc05391e3481c136ab1eaa485461220c09d81b3752b0723c8ace564ec3da70627706d60af4ac0d78811739083a551e461753905e260ba60d2676c51f59494e47928f8914e680924fdee4ed24811a8949d3ec7854b78d0064b434c961a1bb87351481cb79b32b654dbaae0a2d5495c765dc1dfb7be2bdd333ac3d5a2f1af6d142d7c7674a9593af6f6a41623e0458d40662bea404cebc4468e0a9663c940405068fa2e606716fe550768c743ef7a2ea6fcb73a5507ba2db76a4f0c02960a74465ae55454f61a6c30952271e5be38e73212fdf474422301133fbf8a7121cde6d26e08d8f4dc44b6e0ed1d76e46b10f340793ab9d68ec343d29a72c523c71378b19725bb8677a279b0f55b5e73704e01d09555e4b21650fe8527f3aa42ae5da441a797c0717350a687b02c7640903844259cb5fff3df62eed03be0578558d6b790889d9d20314e33334213c74245c736f584be4144e7be78c0acae9265fda30f10e21f5856e0870fd32cc7b381dcc8c6a79a3432c2eb4c81b47a9a33b295908e04f1c75a6192187fd2b955cb633a325a25e14413f6176dbef36f013b70239d26969ba195f232d3dba447d65856a20ad5c60a2cddd2ab31a3272482cba465aa9bf169a8737283686ff2356c5dd490cf67d4e5c3b2019b9657b0cc8f43e2e0c13370f5926025d18931352ebd82740907eeb05f27add13de6a5a672beba528bfb8e0729e69f37612ce5d4c7ab2e86c8bbf8010d5dc9041e216c70dd0e8884bc6a24a2b6f34a63bfc6ba532f0419a76d97f076e3b12291857b18f621277580078140009b0fd172d93d998049e4f907e445c4e7950759801cddec25f18167b2df23cb112a46fb17b748bd02318467945b273656b1d4d4f00c7f7867767c866675527846f10d0aa6d0fc0a15221c0001d36a6060aee78630d1221b46243c3031d78dea20d0793a0695f734e0ff46cc856e8cea00c608dcb1eb9fb8e7abf64c2664001ba291161de6dcf65034b9f52a75be22d75569ecc506bb68b3e56752e450ef803567d6fae292b56aeb5502af829168b21355f4a4e615baca177162a163f26165e0c3e958e2740c788bf6b1330976d5ae31905b5035d6f1e7aae7c10642b5753cd467ae0f2dd39d56af41e6dacb048a90cd8495b8b384da36ff632339977237bcce36f358137635d280a100ab7b82bddbbc60bcf6e3f4fac00a207df54573fe927660c3798f51513610d01927ee11debb1fe3368ee6a3d717c5537955f5f353bd47200250dce17609b5478635f952775a1f143421736219ffbb848a1addc3016d2436cb1cfbf2d771f7d6568ceed6e4abf4b1f12982412cd3fea43a4ea0a740df2e56cfaeca453ca64a20391bf3542f4f80d66eecfec4cf99f5422b40aa6676960975bbc0ed422c5983761fe276c4c2e152e4f9a24aa47f49dc24f8f83f23c7f6313766e702b3ca5fb227b79be7837c3342f55a275114a5e412566da22be2451ceab2941f23a3bb078682cd9d7d32b04fed80160a19d705c66be5eb6e42906a66ee50b0d6f3221b630202ab690be7943669a585313481788a9ac46cbd8e31ffa49f124f7696901e1057419916d026c16003520727d1024b7f19738039c6f5994d033623afd1465e675155fab9d2b05b51a076e7a4e7a1fab962f42278c0c47f3035a1f65d79345da9a305066d40e75ea060436fcd5212b3a7e210d61e0d21e3a93a2188936dc0eb5027920056c81568ad81e3f286ae740ff7a0b75eca24f01e3fdbb3b15d70c47511c325079725673bec1c46005edc8636537de42e6457261c8265b7c45d1db0a39b01b17e40c676b130a0a7021819121b604f63035b7fa1871d79f503dbfc43fde307b4de8b2be6c6033f50f060473752dd7d320c9ded111fddeda0f53e2fe76538f5923d5646d040f55084893734347fb12b71cac66a80c0fae2454f2f6bc47730da137455332038037c832dfc04010ff07bd5d083c530d97b5c40928e8e92ff98faf3a147eea6fb82b064fa40940619954ae4d249a47724204646285c366393e4c225e83b1d4080a8bc9131f1c135f7ab85834308830613594550ca1485c38870d252a3799c80601ddbb2e90dc712c43c7065b64486d0dcb0d5b3842344b2de91d62178cdba77458207b42247a3950e5d90973b368521ec09cc660c751b01bfe4f1f4c58adee00656b3c19c3489c41371fd12ac23db87a0f63c00f6bc43478c3489c41371fd12ac23db87a0f63c00f6bc43478c3489c41371fd12ac23db87a0f63c00f6bc4347817573330c7e83a257d5e692969c9287c12b0fc2c7e1d8367f916321094f2462f363fc1142e9873120fdc3f455506ea7503c7ce4f6e8a5465333848229a7ce360c360863bc62ae02ba8a8ca2fb49c7f0c862b8b6cc7b7bc4cc0b27378fc366c644aa6fa20e3ceda1bbf55d811ee62e222e6677a01483a2f692330671664de9f70d706127eaae424425102575a5ded683a5857d83c23a177027153dc1e1b8aef470edc4c3e744682308f5d70199601242c89c2047761c08803336a4e5b8ae35f0d6aad76596d7bf337330af00aa21c574beb1a1075a46e0b5c2681785071c48a71abbdc0192a17960246f442466b6afe058ba8df142706dd0e079fac33c745f23bcae674214dad3f4a1eaad44780d4f218ab7d7a0b4895a30c58e5810878ea9f6d46cb46787f2ceb4c8569d1271fbe822c82f805026cd5a56cc471796a96ef47773e264f3ce7190b66e4e84640ad786342ca233661aa5c0e644b6f6d37afa5012bd784c6500c43131ff23b77001af94f1fb0f5664579b3f6293750005a9813497bd6a9ff5996223e5b5fea0a2090d0ce0463684d073cc3e821a8c8562f74d6551aec7b9879beb5e81e66ac2f61dcda4902239ef6773a3ab075e181c3697bd9ae21bd23ad0f31258f7a528c113bdcdd1325aa32d818941b334a1c37e62e86896f4773145970ff61bf68228b1961606d9c01719c187b36ceba331f9d801b828c26053e375f7019deb463f05031605a752f53d512391765413123feb69060c578fe000724135c3e55d51d4d681741146cb700a1127514a9fddb726f20ca20db51fc3ac56c200c507561545269e9149a67aa5894cc6a111c76045c334a332922d629642dc6e851b6c170749018aa1dd2d7f3664867ac0da8ee9937e96fe86845cfb9607d55fe7a99b44a6f9d4de7566a910e76401e96798eb90522ecf0475423aee7058d8b864dbfad1246028d4949e44a866fdf147c06a429e54aca37f02aa683de5662c09c2124c14209ddac9b187715b2795906d40284e9e20613c5b61041881218419e1452525df573c1c1784b9fd3563d3d39b15040258154c1e6dc38a65caa38783a0e28e74abd4111561b1b40921b3195bea5404c0b593111ae4d79c154446d71f6aa0520e87b7dd774cf5b8363402e8d094e3e1ec435160090e13808878871649ade7c5b004c14c914386a7018ce3b972ed90965e84f6c35ed0f253c980779c427234a0a3f21776fe3b40d2ceaca7145481004c9495c2edd0b8b3d5819935bd78a0f794ed78f1f0e2a6c6ce6bef347f42ea36499159b1c5314e413f354d27e83d75c56111b7a79cdb03277ac02b8705b43da6a80db061fd16c7f2a1ba97e6b436223678ee840378877e73078d84410bffa5d6d88ac2c2cd863f44399f52869f5a1f41cbb6f5a74fec85e7b6f7c85230015b6599914590915db8c146c4dc437774d6653366c5a651336b21c067e4e4db158537a7c8f243d36252f0c496aff15af529f5c756696001cc44b19b91edd0fdc8c78535d512b14117a5b0b0d137712adf321390c83dc28a002ac056748f91b3e4f9e179fc9010db93c584de3cb810a5d79f641e64bb241305c7743be4ff94e50db3a3a729b4a7ef57da36766882d62ab205b780f0b7f27b22cd31f2de8325a3dadd56aabad1179c083b759e5bc710634c9bc6980f5eb35fd508c139e364e6648828d2a13004557d9e6d24540192570612d6943b9f8c14883ede67942bd2d09924f8779d015f5591673cf26db4fd86b2f5c4f462605e34686dd9d09c3a5e40a9ee1170ade90361559b837560fc28a793876a25b4f554a5b2b4f7665bacce200382f1e73c7de607a2a9593375734127b04984e1869520f683c7d2a6e8be30218b64fd35783f7ae18281b6a23e0c95203107b736dd0110c0a76881d38519265204d60d44b1758190e4c93ca4517676d4a8b240908b1fcd833e7f7021a0c0c843c209d046f6f7590731c63f82d27c3280328ebe465685a3a2a18769966b59e6c04d2fa4b3dab669f54b91fc374a41ae90b340e8d17766296052143ce604068cb6ffeba103fa56132503f0455592f81e2067435d5250f48d45cc90a9f53b97e3c5e2d36786eba39a01dcc536e738ba7dd06846fe15ff71a0a35616d1b002cfca020eefa9c4ba8877a5cc7d52c436851c037e988496734266669ef401c66cd743f2b79f9ce5d736f9f76a7033a6f7bda624ea64ed436f84efa5d8a1fb518be8ecb3667b28c6b3a3f0277692d3862ce2d5262422df96268d4a172e8be7628cfd8b37cdca5bf4f3602004b7fc1c10598fa48091e90fb37b69dc1204f813561bf71b96b4cf24657cc96ec15ed7ec53e6acab368be7ad127bb50e768d7a4fd27444d741a2a81b0208098b7074d6bf6025697e451ae121e2fa922aa7068b2555d52efdc12c4ac15580d9d8d581c81c707609e8d7b8d5f6849976de109cdebc419b055f6177da65522b621fb51af6f6b1c457e4c4eacdf40515f204476447cfa021be45465c5f1693004a37f20474dce5c035473182a2d9f1f8142e80bcea5fd50259cdf46c09acb76705c6a7631d4a02a3393d5511d7a491b7e1aa3685e375378b5e2c65043b6d54bd8ace90ed143420b0ad6c72a969198769eebb12d469e873869324a2d1a8b2536fb96cc292260b842c6c33a1d35dac92e28f9e0679d9b0e2e66c0cc4d84741c7c1e8a97385b7d544cfc4e485949389065631167665f00b943d272ef586f457751b0ce1333d7f5ae2f848706607de61a79a2016f7a81ae5d1ef669d01b6d07337e68a7de427780f70f4b576041aa779c0a9e9dd2552aea0825b036d47d3b5c4723009855749e693d50d8842623841c420c2413f02f36c5e976aaee4e4710a29974339a9a44c95d374d55f8747373e9166126e88737d85ace06bcb5b468a56db4161e21b1377b3b682d2ca22520f5fe621e4db61d17eadb051efe89203d748c735fbca4856738e2b46d188ec01a364fad45200358530ec57b54915f113e9465ac2546de4459d73a85652dfaa424c700602912053c5421182708afc4f67c71b81362ebf2b66caee8d13b047f273c61b9635f506d7325ab99d55bacaa5318a7699727a3c5a36c49439522a27d270da5d88611d012bb2d8a33471c9c259f28ba2d551ad0a29d337c4ced39af73c90f28a6823ffefda5572df3f164177662368efc5a63d286824c088f464fa1b7956ad5a88c732d2dfd0a1b01be6481fc935f89225c2a1980f5567da6b874893cf8550e4dba0c94d577570a481e0416e4de0754a9952bfffbf5587046de60e20db61ca5cf7e4efeffff0100000000000000000000000000000000feffff0100000000000000000000000000000000f4092f26b523721eedb85838b6ee9e1b362570333b955a3ee5bf5f1af466e4191dde604e47faec7012573b79e9f5a0158cf6bf438551d31ade4eed6efeffff0100000000000000000000000000000000feffff01000000000000000000000000000000004915905dcc5a3f173424eb38cc93f11d749f1348941d8d236b910f0cf03d7b58f168033517b66b2754a9952bfffbf5587046de60e20db61ca5cf7e4e41f7f60e58f3722be83c2f7b856024350bc7b26941f7f60e58f3722be83c2f7b856024350bc7b26941f7f60e58f3722be83c2f7b856024350bc7b26937a8f0001da02f632105c0705c14b10633846a2770d2867682e3e36be1a67b77bf2fd331d24df1050bf4160f7e2095208fbf422e2917f7715157305c2d9cf53b1412be4466a0252266679722f2aa4344c23e6a4e0275893aa006ad47c1d6fb506634ec3da7f33d1d1fd5ea5eb2ea72743808a5248c578d701250dc64903f5105a4f10909e7f82f547fd97f791250dc64903f5105a4f10909e7f82f547fd97f7962c4a8107b081b15f1dd72000ff1ba15648abd04a754e32df3f6b4539f367c29314546408e70cd6abd80e1396a63d86ba93bf7763bf76158d6c7830c63297a56894c9b36afbcfb54bf04d914f95ad750a8f72c697347a5727ede7155b30ab20f6d562e37f42a6a691581e36238d68503a228b8520d2aec742f609704142aa95398323543d0b2db2be2c9ab1f1fcf21391addff3dd59f5963ac46da5562a8fc2912000950ed3b3902a75cf82c9546fa384fb6fc1595140e6f05375e30d0aa9148cab7cc0b5c393c2436e0e21e73bc78036a73b8294e350d6250b352323b0e7f503ac9846e319d2b55673c77628476652041ea6a3314b2a93dbbdb02681af0a83f6190104c9744e4039d2ac955fc51d74a385fc666e4866e1317f1353a683b0a4226b00c53556786768df9802ec3e7626a9f639468168973074273a85a8ed15258983eb3378a10f61b50144076704bb004f5a18640efaf6920b14c92430652ec5f60ed8e304c096046a04e1423b8b24852b97c8f3d1ae1284f1be3530780616339817efd166b0cc7755b48847a4b28f231619f4b01ebd7524618dd360236737512a48e0e6e34bfdf4ee5690e5438144d6df1874e21f2d5794249ef823eb7b65c191ef27e6ed70ea27e33bcd663b8d2664d042b990d0103da36dfa7ef5a59ee99526c3d595cc566d378fb9a201b63b85e4f1c3aac2a7be0fc683a4df33e0932a803255b8e2fb22ec037f9f6306690aa0c0d1e0fd240afa76239bda10a0366e49c381af43938a689440f68da131fd67dca0d05f5ed65d75d804ebe6a3e495fe4ab23e2a8fb66341e04335847ba64076ca95c4c2a3a7e32385f5c377140637016515589e94773eab7a050a6d5dc09afdad6117dfc982a0eed0440e9aa2659b8f4b333feffff0100000000000000000000000000000000feffff01000000000000000000000000000000001b658b1d08a2d24d1bc23534941aa76790685d28eeaa6d1fcbc78f2ed8d04e0885ae6a1d7a4b3f41c86283081830f5187082992d9f35542227ae9117afdad6117dfc982a0eed0440e9aa2659b8f4b3339409897d9574b46564343338c0a6dd41114eca269409897d9574b46564343338c0a6dd41114eca269409897d9574b46564343338c0a6dd41114eca266dcf987c45df1c571b44fd65288f3424d0709530d419820ddf7a8c03612697466cf5b10abbac8d1a865e5e47329b6324db80594b7ae9111ed878c22af6ec1e338709b9141a90e02a09b1c24e4bd930295e06d3584615a94ae49bad02e5f3de7dbcf25e5ac4a2a7550b3a121575d9b60e80dd2e07b06bbd19deef882077b5246d19fc4b384ed0dd47b3381b00e76f6e2e76c0fb7936539f71133bda48d3b93b0b0e291f05192b913dbfbb4859f1f4ba47c0ec877b3d67f51a4020860ba5a6c619b9c9d10d21c7b91b2f7886401472b27b88b58e150c47292a55192167bf17af554b655748e632c623003c1b4ce945c57d182ff225d0169a4a0df25534fcdf6758c28b297201ebec1ebf1d1633c99a1151ee70a56ec6ee1d12f047122b44410e08c1e93a2045fd9822c9a4a60a80965467ae2b503f6990107594ed3b2fb330f20a7330c56aee9a4220871e2860a6e46208d67c662e320feb1d422c2863449ec74fe57aa6354273537496dd637aab512a1271508b38042dd776c8dd086a21656a3e1ba36511b5a75f313dc458109befc548e679810d06057101fe2cf424ead4510c580ab72224674125df4eea31e593d565a198270a5912990db4b492581277d95b62735b1123c6721ee290e47040a73729791680745dc838494d9f230f26470f0346807b6bbb3ed724b01a5743b0752540dbd2c777dd50e542acbf09347222f86f201f9f7d42652f0c0a9cda64da9d635273d1d847711acf23a6ab263aefb86b6fafe55972ff4b0513f677ba16fbbe4221fc611d779840f83d60a47c23358e9a17f00af530a9ec9101b32a910c02e9b52a2f6a8944b01c1616fbfbf3149162dd3784013360f95f5d0327c9c146068dfd38a4a1cf426d84664f0719fd311968075b6b14745027d8b50bb39ffd0344388d6180d2b667e60078359137345a855c1879774f387946414358a89c82061062985e33fc89576ac10e1d725a584855024704a6114207fa4ad16c11b99346e8328f46b394223bfaa5141d4ef4a21a3d0a2e0d4db9963e4584ba451f5fa36e4ace6b62a3779659a56f80118b29930c4bc6e56b5031211fdf9ffa33e25f630807c52e2a173a38262f596238b2743c496e877c251a9d8360fb2dbe0cba2e74578562c352786657294dc09070d921a83e9e0e387676edd92f8e2cee09aade4a5daae32867a6deeb452c82761e4af8be7d97e6e41ba5779e45b56e743ea185f164cadb314ef54f756763169d2c6cce53705fb62f4ec08f83608811f27cd0e02740e43a7f143b7d7c489ab5b44e9804006747f30f4cfaf4aa6c3371b551b77fae6a8cba6d281d525515ad43007e3dea222d1636bb53090ac763a60c0c3bcf14555c6917b94f2cd1cb03cfac533042beb954753826222023421dc7ad16070c14570ed62abe793f12f9145e250d0a39f16434a84c1901dbf930704bb54c51e5263a64572828331dd73f2580ade361e91e3437b4c9c14f8c80f71b0864e66bf661082003152326109cd711615d4829b68730738806fd5d11ca601a8d37704e70790e6296e72d70db341d5f890b3c41f367b928e6ae3e1de7ed091c60c091250d32796774587a2bfaedc91290cf6f2af911fe2a72ae883b7f707179e502717d5391464f7b09ba7031552143a0a2402e1869e445cc751c447d2f460b22b7900225a0841c8c4d26567669f60e7de74d0e7a5f2a086e18fb12beeeb41f7784c45ca5b6be1dfd4e542b413fc93c34b4881167dcde2e03d13f6e18a2945e30b9666f5280025a3d5a27019b5aa70aac9ac50a67aa887cebbf5154b59f703d04922e77badb726fad28343fbe45b6449ebd7479bbb35856110934798d480550243e2a243d47582147dfd257c7d3d756ba559b429480e00851a3e3334653557876977f2e70e85251a9fdc87cdea4c9542fdaef081817b55c4aacd23592f18c662887de50a34dfa354631f01dbbb7d776c20bef55cc893f16a619971d3385ef360f5154025a2e79500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000867cd7142080ef7249366e330f48f82839664a5ceeb34026f8c22a641974a730d710b50a89558f7e57c0ad0e67630d459e803331be8caa6247b55d46ead907423e39886df95eff0c92b6323e06a8a32db40c7b5196661d5be24234063ce06c52db3eb357ffe36027ee4b09591771a51d38aae661525f244502f7a614732a2b5b10de8644089dcd6a5cf8fa63ceb9dd65ee30dc1ef48a96100857eb3c3f3f71388d06cf3c221444543c9f8234c85b8206be97540eca2f7c3069509d726c336d75e28fd055d2db827542a235799379425cf344174460ea9f56bc03774100000000000000000000000000000000000000002f5e844ee336d76d4b5528393edad9791f2135650d50047ec087d051b64a0d623a5a827ec57e7833c2f42b73f7fab774dd5c85029ae0e8777cbd436b57c58c39cd96eb390b0bb55d80b66b57300b1f61373eca1aaee0a5279f54e246cfd1935b95a563622271c53ae2a50e69025ce7688d33ce2dcdf71c7a916b7f52bc4e7b47244fed33742c9531ac2f73480bcf554556f18e33751a7307dc311373ec719f3b8bf1f01ac9f61251be47ff50b5eaa444db10923b322109018361291a18b2bc2b1921ec6bf7f28136542b2c235f0cc903aea9f915dbf92909c699170100000000000000000000000000000000000000009e68d31c91b2335f6ce2a507433ed312a910f85e2b2c300eddcb6f66bc4b1b7356356e7a61c24f75a0dbab0a7635e1384f32107d372d411fff08f73ba717501c1a8c150f1ea43e49dd80a37d7b167a3a0c373b4a6dbc82004ac9575c3361b81bc027622137083c72f94d5c04c1a3485083aa7d71b0f6a208fb41dd6db6dfc107f0db1521ddb4bf243d81073ee178e908e2edf24a67e6365c91d8584749d6c97eac5c744f526e5475a108b47971e0996bdc34ce7e4e5e832129701548aa2a9f3c5c6bc62c618aa704d1612d60ed2b633a8237124ecc8cec683b72d6710000000000000000000000000000000000000000764e1f4a7cd4e60a46f8cf03a1ebe8737488ce21c8d5227a85f8523da793643c559b215f2141ae74dd855227d736033d8b4a866066736d516c6ef93521289a754da61f5b22159d22dbc9fa35948a2069e445e644b54efa555a2c786e66b63d676eb8b46e104d233ee8987926236ea574356eda53c11c3d1cb5b5420976352e1c8f299b162ca1e13289c3b55a8c7d5a1cb0f25c015f7e1a7eaf7cab45dbe4e46a555d3e564dd3dc66046a7d41967c490d7e33d002759f3866c374302193696e27f14b3d5518ce5001556195295ddd292403bbfb238029a27579d53e1400000000000000000000000000000000000000005eaa795f222e1563bad5b471117e720b5bdbd14c34582879d6e9c66390ccf34f83608a385e727d24ee7109427ac48718f1cf0d270021564e0a19cb735940f82ee51e316dcc201319e0494368531ecd76e5c0743970a3327a2a8cd214d17c1f3f9d28904db02b344fe2a09763129e13668d397f22dac63f285c552b71ef48252939b13c59ca3c54500fbe990463212b6b1eb5041ea64e9f79170fef6a6733041a67e7a07b78a271516238ae3439611740d7f50a5c0db72a1dc4fb564ebe7d2e27604bd2272273ec08aff791695f09bc0f5a89c83d8bf7512b144dad34000000000000000000000000000000000000000051d00a234ad69c7dbd72d91dc502f54d6923226fe2e2b06ccb10c3053c53b71bfeb6f4482e1b4a510000000000000000000000000000000000000000feffff010000000000000000000000000000000063b3bb5f14e75f04f9c59039c2b9e917963e6c41e935127a5d994678df5b5c2ae5cf910cd7597d411efa1666e1b2d64f542f12118dc40d4800fb3d5f306565231cd89532c2ce2777c4e37d5c634e0e309d09a44f76c00e58a6a1e64f8632193c0962d932ce1a167aa9ed8a24a6196706c870bf79e00069463cae305e7b8d435e31879c2bad7027630857ac0bd6e32910e886e02840972e2649fc3b48a7150e5e000000000000000000000000000000000000000012ea2c7d67a99266355c390b2c33764ea6c326080d5bb603c048c4221f2578408d83c245025902791747f301058b1f6f19ee7320c387dc5d03f1d66886656d4030811329e5858d3ccaffad211e89c3603040a0753782926afdff2a78982e92117c2b471c6059255835e089300664623a0d09773926ef756bd93b6d53e6736e0f5c9236032a519a6582964a169697f84b24659821338e0268e75bf93ddbfcfd10000000000000000000000000000000000000000053f0b037f2bf1055e8a9bb133df82e7560919c262964cf4817e024477d92706063b92b0d98b37f6131e6c1175f08853302f3005e1ccd1b6b0bdf1c27a0f8f5639eb06c3cdda30d473d152e63fee16e15011676227a27f93eb6238d2ccd8d6e3d191b7e2a0b49f2537a5a4857cad3b20439b8c33dd7b5703d5427080db14ef6248b69e01560a6002d5403822b6bad6d576eed190ac94d6a47814b2a7897053f0480bfa36990f2303b2dfc791a59b1dd19db4dee7a8c9ecf47ad1c102a83472905164cbf6f94d0b144673f1b672815154c1ebb60479029990744c7281623deae7598cfb53c71a6c22b67a3e6610c30b82bb215290211d95a5129d06129267d341224605a0622563c0dadfb4e365f45330d765ed617599aae437f3dae233932b70211d2c2383da2ad3105aad526ef870e1c3069f860a90df52e39efaf26b361247613d5ef444b65294ef8ed067c179d420e51eaaa01274ce133d49e93091f0fe0329641301b5fb54530da66ad078f3eeb4a81d45e6ffde2e76697783b597d607e6aaabb9335bf678f2cdb975220609f2d2dd06803259d7bf021c24cc71e574b1f174bd6d65f16685d58b8a07b36c0ce7a766d6d5b13e25f8c68c7b80b3a53c38b0c7d4f90769f674e348e4617000ef9e870a84521454772bc64dfe19727bf90dd518af1a242a545fa350ffbbf642ce36f2d8fd9c54d9503617516f0ee647913e002a637666a89fabb6ce35296782a522c79b8f94933310bc4061a599e2537297206d86e6152e21cb63d11a6fd0fbeef122098cea26bdb59930255c9823d6cd61938b7a4614c7078050e17b67a0267922b66279bbe719c203a2a32dbce43fe6ab75d5051382b494baf2d82c52e08090c7d0bbfd75020ca02865179063d0f0681a8199065c030ce7589746b9302787060c64a5165a35e8201ff542fc5af6bb9b4a67873e35f2c7bc6072e7554997ee1674936ce39617d126bb21fb99fdb7c86bdc368170172469d01d94c3d08e262b47dc84035c46f74ed6e9744f78dd30417ba626faf744563a4271a1aaeaeac18725c5c2020909a5e800b7a600751d96836780a4b3359d7248c2a422b1a997945bdbcf00400afbf44f0e3c3081b463a666b47d1541271c2756332d84e6170d120fc095c07256dc846007d91092ded384f47a6494214221d223bf4ac4696a1db5196ff46093e3a183b142214103ee4bc1825cbe308d462fa2597b19f0a6513185df8b2d520709f5772f2307429dcd88417ee047e4d1fa06a7234c318797507c818e7fafd5bc98b875b4d0a6a796201821cc0a16d4fac127123a6a6775522a8306f6a808a18395a454ad964ad07486f5d3aebf111283896df4cb017ee323e05bb3f4849d96e0afbcf377ecdaf37bdf35945a3b4c3071c89c208192f973de5beff65c0ee265761211f0b68ebd11707b2d850e9faea6aa8c11e1b333acb16279c357b71ee651960208f104be8ec5778e6362f2b70fd6c611e8f0afa6a942a22d25e2148c9f45b1fe755134ed8913f4b66ab7eaf3680304ea66a5537f30b36244cc25463101d5783d40813e1c9f15182aa611b239abc5ab4570f42630c5066b26b083a8c17ab53d2377d0a3809e95c80f69f60b57283221af6ae0251489c6f22d6c8537c7ff8310479f444cd0324396f5863355e7c1e3f498cc83a2fb902386f486c34d99b7405a2851f4ed338370b831bbc1079e72e1ed311a76d420f5f3943ac761278b1c069ecb46f4519a0d01e540f330bf40ca33d0b7b2009c599a34c9239c43ac7cd9e205fdfde1f6653da0e98dd4b1b770be769e9371d6a121d350f60496206acc7a8406a151125dfbaee4c5371b82f4b0db036a2be4149b6e95300368503406554d14a3712ed6a93ab4f7646248b0bf4f312515ff2d808ed70aa7789ecc102ff76d67125932232808d1307dcba5218af6830750424e81d5939e22c4ab2ca0eda3154316435ec0c8fb89267ab9489267d17517c15e8b84cb964a964a11c91181185604281e8c53befb3c20881622c0b9c06506308bba1726f73473bd15366608dcdfd6b48158137b19c7e11c2e6e12f7b12242f708302799303d64e25322463ff53ee6aee491c543203b8715c62ab0c2929de25f361ff114590413f82d6506453d6765f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048ed525cf141805168b7321aa97e521dbaeb3317723c551dad9deb499209530784d212019b33064214e6470c293af4445ead3c0502e3777e292094294b1f106cf3a4442f9269a558a023887daa96c0664b1f106cf3a4442f9269a558a023887daa96c0664b1f106cf3a4442f9269a558a023887daa96c0664b1f106cf3a4442f9269a558a023887daa96c0664b1f106cf3a4442f9269a558a023887daa96c0665a545b63c4f10826fb0c45700883274dc1db5542ceb01423b50e2029a909196bd760f300107ba655ecaec221dc80c1726f5995613302db4cb620e74a6fa1c54ed7d0ea1a31d50e50c9d6492367de5c4b20607f42e9a02e0ddbf99849684fe01387a7a400c4717437b96a7f0d242a83791b5ef41714bd082fc8724811eaf1a6469c02196bb9b1515ac62fd90dccfec00d1ceaa26f62f0790698503b33dbca535e99f47f5a4ca4857103c9740479425e69ee20ba1a5b4f50409f28987a49df03323ebb9502121b7706aba45929235e9101b573ce4d0eb4d350247fc75e4ae43d120b0adf5b504ecf31e6cd9705b6e450177c483e1fc2a9326f96485a55514ee21a72280f35ed86680ece58c41e4997130b717d34752e676677cf9c717cd886f90f73489a52fce9912feea9b76eb542cf0edb963e4f77d10b1cb089f6675698777ecabd9e627aa21578a82505350da0563a4af2b636cd3c19456b4c6659d8e9ef7e57b7d9368d7379718f2f0f717528857490435b53770d9d1c7f8fcd5fd5e7861f890aff6783f3d6392de2e24f935711763a1a8937217d63272855ec1fba24036d86f8265848ae7a68ff04d94c41235410e4219c1c23aa1535c7e72322d7d3ef55ccb50e10d306490d65be9b314955ef4286ea9a77dd1fc250d45e09335668842ef529db2e7af6796a7e36036926b7db648f2bdd6b37c871767593195c05c9d252c646fb1a2bd8eb360000000000000000000000000000000000000000feffff01000000000000000000000000000000005cf24c75ee89934782ffd53cebfdd600b903c9230142266fb73d63226bb3ef0b15b7b42d2a60b209a9000345a396b61bcd87cb31290dd86f169f7d11803fb365bbcf6518cc73ed5c9239ac1bb5af074d7d16d06322597570a253837b24c2c2012d381f211fcbcf0fc32b7f6977ce4c1842573e67208001200319f64c27b12a70dd662125c4768b0349c9c838f10c045c5e7a871848f7437336806c18bde32a3b0000000000000000000000000000000000000000accbb92d96b6f46b9eb39348d9dd9d1a1052bf21fc68f951f9590b096e83f64c85a6e709c705b619374d2d0c4147d01441eb6d07190ebc78da7a26762e84e2007efe5a6a29822d0b73b232504b1f6d32ae6d6b1f2ada1652943a8a042f9ea0539202ad2e0db09c1f25ad0162de00bf6606071121f8a1411bf7ef6758833c424fba66aa25d293bb22a47ae76584abca3a24dfa5343cf31030d2dd5e23ecc4462b000000000000000000000000000000000000000064116c39a37add135ae6276589eb524a9cd1cb51cfb3104e423eb750e9dd295df0b743700e76181f67e02300b30fcb6561e8df5ff3c4377c810207756a6f443f493f31322f8223267a61c47e3f71c35911a1234b2ed0e9729c72791b74145e6264b9934d0d35724f35182d08ab158b5ad3e183406c640940931307484f29894383e95d7564156e7302387a1725bb60598f344428eb6fde3467d7c12cc7a9c11150846f646b0c2166fea334453984c5425f24234fbcd40d58c2f3f963bfb48f4103298663c74fa52beb22606ded554c708924f474d8341f7672e31b300d775871c54f83532509754f3198804e71d9e8109b1ee152b7ad4b261a869f2f9a74950927070a07494f00127505d132f04284261f50c134389998511c18ea60e89d134246bf9e690975eb503e27e662f533d05c8895191ec03f8a6d80edde32bcceca418869554d4899fb3742b65409e23de32270cfbe2854cdb9453772e822d2df010aecf0651fdda3e450b5bcc5530bc86a051331f103a1f6907387726664815b807380f6b810545f122bced96d6ae9f3b9197b970e5ca45b5e535cbe7c139c71ea5d6638fd469931ed4f801e3a162e4cee64e46cf72756f603403fe60706f265fc039fb31e3493511c47b64f156fa662267577816806de562705099f846810ca8627c4638237eee74010c095600ceb6603121de9cb5bfc73042f1d7d5554b278411c59cf460d01935f1ce7bb3b6e16c48b6799ef254991cbf35bb298205633a0324fb25f1f4b5224de73cdafe87b5e068e17c5648b04df518670fb56085da064ce789ffeb061b5e8b650dfb76c6e3e89824829e107116858d77a13caeb374c376f4d4977ff0d9aff5b6a39f5e67bd4e74e22b275722563c3c8283386105a81d45b0cf674fb6428cadd6cca43ee2c315e086a5d275e409dfed51cca7e5c0b98cbd03db8f9c32c4f948773c07d6f5d75ceff0abbd7222cecffb446cf5a382618050f08cc3ddf00917dd40ccdb14848c4f1e679885c8f2f72acbd1fcd3c5362a32c1f154c47d876c8bf6125b1bcce7c49898401ac978b2138ec0a37b977012f349b39692e19e2207fd6db6c58c7eb05e846de35f66cf44e53ec1e2392358f0f73ccc05e32315825a17b9367f1d2d40c2558ed6bee8a7038132241634e99cd0bd7820226ecc44b7d1247943ae58aa659b26ee1771dd1a75d77d3f94ea90a2513af033f6895156776c47bf619a34c373b3a5e390df1acd03b39381f594b6da72623068c7913a02324c30d6c3dc16ab66712914e313c1e566fa1a6801f43711e11dd87960966fd6916ac514f10bb018008d95c787ec0e55f740283af2909c05d03d3b22b660547110dcaf09c53d0c30e4f2739113d3a737c5de0a4302e9311382dd8ce8b3526aaa878a536093fd5e2d42607a3b45028c01f28b9ef8320be57340f12489116b6b09a7a72f1525cab0126354a03aa5a8b831f71a82db578328e4c02c8125d03c8a2ac7be6d6a2070f60386f8b374f5ea3c2474b4ff6c5665a6f22508be4f04335784573a577167973323320ba35e31072892b5c9a9c7f0e0171d16247fe45524b08ac1e012df558b1890062a647c32814d2015890c32a114713f62e8842247d4d66df39eefd15089b60101c49548e38e52f022b2d951634f15e0b3f2900d06d3d705f40445b1223d792fa13dc665d5216db063ec0327d4cef4c4b5529ed1f2dc7084e0547be4d2b98d4044e4a221d0478458e01c61a47171dfe55193462cc3b7ac85f7966a6dc11f6a48966113ffa6b65f4126df8bb1d2011cdc66058372914bab6575905f7532cee9752315e6b3171e7f3524f9a522e1ff6a2142809a6c5388012c77559b74059058b8f452ec245493739ca3737f2a470ed1446041c407c39e7f8120b08daaa2720534b6e802e591c3db9570e74d161735764d818550ec36474dd1f6a15ea421daff6a951382b697238fd8042b563446266ce2d28b31d29228679d4780751b072542ea53fbe7f9f3a8a850f6982b2e54f13f6456c6d03ad1fea0a020414569a6d9eba3a38e3a2de258f32863ba3e8fb614b7dad183d69a201d8dbdf3ef80e184009776f6f81adbe3b7643411865b9b15a87da0a299c8561138517701cf1913b1fb96d5201bbf84f41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002714753b1a1f2c241a99c75e0d297271fc144513b1e7821b0ea2796267befd376169766133190f23222ee541b6f4fd2605dfc9377c5ccb1953324c64c620e72fb64f5427f81b2742b1a27d040828ed700a01016b4c8a852b1ad55b3985961431a8cff30a0915ae1e37cf3276a6b1685c14e7036ff9dd9f575feb915dd0631b4de17df4369c308054a66ca559a2882a6a7a900c63ef01fb7efc5b430ce1818f5cc4fdf00e8b666a50de86d874adab3314cf1ce418ce15f2218bd8fd122f07177c0ad57743a3637068e530437d80efc90a02153b4396fe8b17dd70370623b4cc341bd69a12847d682bfbf4ca6c6a03b4006b8d2f187248b56dd58a8c3f388e46609bb9ea6e1dce9a5e60d4ef0f1142177bfb0c661783dd767919358e561db89b0e2a1be156efa7e7786107025ec106042a1f8ecd4690f7cb36526311300d8287712c14657b900b0716fd73ee3cb6123c33828ef1114cf3da18ea98d203bc656b6770f36707a8205e54a1c97a6f3c323364adc19c7d2decc15a632fef4442929d42b2bd7302fd52de1097658f4edcb2b54ad7746f279f64f724a6516f5f0f40716de2e9f9590c93b12845693a254f9ad07698370024b2696b7d380d7976ee658d41cd82b937d8def537fc14ad72eeacc76ce46cfc6062ca6c0cdf43717249b3b43202b0c90e81c130240fb64449c20a28734a1105145b71a156ca830f6cd963a30fbdbbc80aac370b70f41e186c3fdf602183d28c64059dab682d88914e5d7e5d4c02445a665e475522f01b2c444d1ebc040131090afdc11c145f6c534462ed885df62231313202af4caac5361ac9673923c4853268c2914220aa0aa71e9e180f4c6fe68466db1d7f76052da068011cc350024703131705132306d5a429838d5651a1c93f3ddf32594b0bef57234a049677cda685171e60f8377033f212202c2f58caf41821ff89834019f40356e2a4764832839b6d8b1f745abd89425accb45901882031119547df518dc18b34268cd3497f3beb0139c5c93e3ec83824822a204621d4c72e9b7daa375a88ee50df70d5043733687dd3e65909c8c9d2675431fb5f9f8f1c2644f3080b0ef7d90ab894fa532aa91917874c7354531cd903b0849f23e69ef551d5943a26d4ac3a6f40cdd320b4c4537787a73d794a19e216abd03f6fddfde76f4d7880424fa18a4e43d00437e6fd0f3c652667690e4a25430281fa3d847a422e1861631925e8804186f8964305749c274d9f0b7ec2ab375b7067b472f72dd221cc970a75cc52bf4f8a22b10b7006e32f80816222e8e2c26f5c91f011f75fdc105ed6c4270d39484f53ea4d079933f33d56fffa17d5e0ab2820a57f150b36900a0a581c600bb8dc7b2e8c3d162a9cf7018711a05e42ca33081d1315147e0d334b3682301499f1d621b1cda432be864936d3a4ae6a98ecd85074ca9b2e1cdf4674f117bb2bc1cf1b42338da665e1d249113835b13d2a2d8534e6d7ba39b9795c796025fc0a2e6903081d903011f1a03832ab7c5c3c75ad1735cef7196694b7154a968a032e0ee09c2eced52939730bb71fb0ddaa79453ff475ced31b30ee6d4123c4d3123e2c111b663a2fb73038eccf12bb374f55fc1079513d0e7560bb3c842ed1d845376c864b23083e263d2952c91a7e11a023f848d6468e4db561566d8d3966f8fd09ef29712600ed662233f5f94d7dc18f5e5d5c4c3cb8200a2c99d93b73e5ca15673c2eb225072701096499d93463ec73444e055e6279bcb10bb107d005e0b2787727b151059af6366666981d43667100643bd5c90cfe893632a17e21321638d1473493f9487049135b0163340410352152df45722f06d25e78e987912221154257db8ba16a16eef0127ebb4d15bb7a4e284522460c51a040426a7ffa171fe5e0794cfb13709943bd3f8f386e20169cc66422934521d1364d04f005eb67cfc56037c3a1302411af8d301d65e05897821f018ecb936277b9a43a621aca3b3890204f1c388b65024898149ea8dd3b741bd654aa37927cc6b44201dbcf6c316d6bd34cd2f74e2858cba63c0e81b023cacdd83cc9071d7b8a174c1594438d0c2e0e9964c00c806c5154c45a3d61f315b5f6ca4d946b756dc36f1366e0ae924e5e610f23931cff36e544c042c96d554250080a73afdf01634505ac606114a44a076b7c78b9ff5b2c2ae9a7446edbe6047c849130afa92e37ec48fd1853fa9b1718890343b679b46dcf9a5d31bf58b27c0f298b03fb96d41301473103ade3da180937a90b6099c3109cfea612e100217a991b4c28184a161f97110462f8120e64fde20d12ae8c070ccd54270ac94458354d7c287e420b4a7c282a8d57b5dcca7194bfa14f06be7e4d73d8df568ff3e32f09433b3f74bbb122bfd88c6c9193833ab7c5df77691d9c412dbeb17362bce801f05ffe49e3da5c5833775107ffd90c4c7d7fe86b4bbbc9507dcf4a720dc968383d0cfa37e9aaa62751e70f10907580346aa689276d3f4b633f05c144b7031b4577bbbb5eacaeeb7e973c4e6231de0733bbf3785a1448c93ee82218598491bc2c5cb32333ce7e6f1ffc7ab74e89c3076ac9368135ab18065eef787719e15e4650d3378d4255a069580ca15a6a4f4cfd7d317ef848a8ffa340d69e577298cf8d0a4590893d52d4f963a9d1d05c614c6f5e8e47fc5cc0fe6d17a7fec66a77996360eb12732cece41e5ef91aab66e4aaf5528e376e526c8fb35fe62cce1c7d874c1ff849f35566548f7cdf2fd34d33cc0c4761158471e06c6866e563165ebb63fe477368b510c3242e7d4c91b0337f624622c33ab8572725e60e5406da2777272c717600e1239317bb54c81f9b73ba83b4180ac7ef68099184766ad974141a5c012816e55a39f860e731165cbb523b8535103f497b45cdbe804392acd7607476da011ab2ed04795e6a28c7376c366e299c4ac157b236c6dd590054ebd0083ea1d41eadaa4372b69ced3ed7af383c3228a73f8e36be30e3d17c2aee7bb833c527671cc8feea79713f3c31eb89514aee0122105074882f561e7b5940c3ce4b38699106e2379d254f2f8a7580ea194ba309110cde805b34f6505b3439a0cf35cb7f9751ee8d566e83b41221d77fe7452ec9fe15757c0614321be37e324344169893c024f893a31ec539625479ef8b62ef7a745986996b12f045361505c0d460ad085a452223bf3813a0e417e3b96d72d613f13a755e7748dcd1a75dbdb3a014dcc95045dd9e3559ad188478fb0a6a1df1332d7df0110e77cea42c10c8c5ac02af78cf6db64df107837e600e76c68e104261f03df5f4724e3a576216606ec963795ea21880414a6ce3b25a7b4a5b0702ba939c46567b51205de2f858ce61d25931a83d4602af634e27860d2e4e509b1c0ee41d6df567b3476b78826aeff52525d2114154f32849743fbf6366bbf34f1fa1e26861afdb0b6cb3ccc64ff9fd2a4850de6921d14a2d5d84207153e271750591addc54a128dd1071218b4e99962d11265a8e1a91e8a160c70c9a7dd839b535e6d341334f6ca7668819025966675c4ca4ea6e0d53d3aa416ee4026bb0b8867a66d35b66fe2c122fe990ea67d2754d5a91045333933b1a5cc225f5335a0ce915c2754d6a91045333933b1a5cc225f5335a0ce915b2754d7a91045333933b1a5cc225f5335a0ce915665ac76a431f322a4b436d37dca1f4041f80dd14565ac77a431f322a4b436d37dca1f4041f80dd1438a1f52834aa9d619e792a2ab771177ba28e86201a4c755c59f64206f4103f237bd5b45b27b6922cbce19e0c277f732381a6e96c092ba220cc1fc75baa0e2d38a73897484697721d39d8a06e5cfc9408fffbb30b01af781818b3dc32e95e237594e0d1374c27fb24b736827c4cf7ad164bc85763bc9ae13cfadafb201b1ff006868e81051723de0a26368618db59530d599d466c91275b546975583eaebfe056a4ad097e54ff9c36689f0b67c4353954aff8301fcb89340c3d82190d26d6722005dd0d26fcaebe7644fba907ba52c84c15da400014591700d590247291b6b94798f80428b3666b558a23460e7bad593e5dd80b1cdf758e2209a6ab5c1083881d2fbdcb09c5f9e574e93aef15943b724e6aa52b2317471c34ec0e4b0d7430c46954c5d96401bc635f953b7f32c366355d0d2f7650687036335e925f62a141730c5332701e909ea175edc8e256a4e30721dca12272d8f317187714630072ab7b5be7c5f52d29efe8481674702a6df00369d342495670e3de28f089f60fb7e59f2fecff8361820d8a719c580a22739415061de8012217d0ef15bbdb5b3c3973c21a4426f47c727a03304613ff0483cd9e1ad539d96b4ef59c2018b23a50fe591046ccf6f71617923a1e9c412e75079de21823faaa3875b87d64c16839279d4fd628eafc7c65d8ec9430a41dd145ec1e7149a9d53c57bd82cc27031d15283bece35b7f9f7c202181fe72742a700928fcbc513c23f57d726fdd580da3ac546570590d9026033ff20af81abef4e977f5c34c2ef420941505642759ddf0887d5f345269d7e536498c08ab52c991ec07b69ddd75b07470156298616c96e66b2c76d5612096de026470bbb92b14bdb526b2834f6421c53b2bd1219f4a40df55315a8b95427343b918db4ad617fb2a5d14fe1ddf5fd37b2534e74b107d4525b93faadb6a1d86022e54b0adfd1daba364339a08cf2558674c2ee1ddb82113d6b20da6cb280c9cc26d5e76c0c531bd2b65347c251e1624bce849099c023722a1904562a1da01e786e77a4810de5c5a557e649bf80c5a1b867c521d29fe6a2fb9e66d88e82c005026215225de200b74ad7a10927f87440d577d1df5a2396b5171545607eb855d1009ff43339132275280e560e4ad5d56caa086410cbe9959ee601e3dc7a101417f1420682c9c6c3de09a95329635e62d0d5f6d3ea9980205a2a68602109e2e6276cacb0440a37e132003501a825ead7b4b5375797af437109d52ec230883b355cac39f66fb3d2a25a844d345d58646492585687cba02926fc0507d627e07da42e56bef293d1f075eca9d93056d151d50e8036f70cc22b13314773f1157da3d060068855ee8b18e0f4b2f903182d8907e609f7e6489063f4b4068e0571b7f50467e5d4e3270a4a21561acdd19ee27ff317a3b5e6b30c3982768fb1013b988871b3aae2f5d718e1a297bcc8b12733df26ffd647234f62e260c4301c6765501e32774a1991efc44d807ce3a3d2c3ba5c234e5ff4b3afc8f2a2996b43d40e9ccc314a5c6c83d7313ad0339196824e9f7fa3a9c73e668965470171b321a6b478faa5b7da29949a7a74d170e49cb34b3fbde691583767bbb85cc247163f7388fbab47837508f27e402a350e500f83d95b4e65f2bd71f7d5b251357b5e56b25e6da141aad6abd60396e90010023e471d9a2982e9cb0a86ba9c82254132d004cda2d9152a92e053bcf909a510a4db115eefdb30ab0b4e8122f844f16c98de14a9c200f3b37338f7909998143edee70199e41666be3d58a4a3be67e40d769310b2efd5603fda4fc2aa1e7492b0fa14e3c4c8f356c1e978853cdad8f65644930709c0cc0023dc2a87e70c50358d3c0532592d3080172f5674c83f6bd62252fb032c855d53337cd3f150e692a77f160bb0bcb493d1bffd7204d866ab4329ced834c3d640675876f3c33ff9a0145310a0d6bd347c068dea1bc60f05a3327ce9c147334ede571ddb01022414bfd7440215a2affd334278cb32406164e861d71577f4fccf99022faf1d6256d3f3408fa943b733b415442ce1948294961867dd856e956d76c47663b86d26a7119673057acbb72728843548c37975ef78c444d48b4cf4d5edebc6b3ec7293be3dd2918afa45935388cc14fc34d7404a4d0d466daa87e21300c8e192daa455c8ed6117ecd345d26f8a28d4a1364bb7a1079c21ef4f4a6260399535001632a272631c32325ab1b2a2dbf056830a4a567b039fa5687a1643adb18950b95cddf502d7bc81a52e1401ebeecd8725d3b1a2763679979b4f88530d6075743ef57f663eb4667278185002644d3f82e92274138515aed10f09521741ae5d342603fe5616a6e5e71e2bca2282d2ae00b81e271163a2e392ab172441a635de527e68b594c52060c1a463e2b46aa33b0196d71c42346b61a62133baf1afad2d5139aab5a3c0f0bd5341c1c3d09a734711e8d7beb59d9687c0c457d3d7d0c11922553d1c46d1ed97e300f760e394ff5b735cfe639228158af350886ef1496c5e35d6108044a82ff9f2f11088464abe01506de550c146b0cb0341b13890d38495d1cd27c334d8e1b567b70b77e548249095eeb318f5c38857059a0f8db519aa7986770074117c3e440763cc6866e3802b1241d7f6768e348ae3ab081bf10f555aa6c24d4ea1dfa9cd1305a07603cd82c7b66ecb9ee4c56fdd367aa82171d0f39b76844542910d364d21dded88f5bae68c06ed2e8ae4cafcefe6919cf967513310b5aca24e156e515e918b465fe3e8e04ac46d4118a65b02b9d0fb131a452438b4970fe230d1e12fddb214458ec2ee681b1536a7938790b3f200235bdd03fc8ec886e0472b55033d24636ef3d855ce156ad612178534a2a93b62ba0d71914a98c9e0b9635db5a557f807679728a3096269f35fc6236398d94ce2c19b44e61af0ef660701400603f1d0a3650cdf24311a42225096ff04cb3a5a9585b35740534317c52886abb48ab9ef95bc8ac6f6ed0bb1c5822488a4ae3b2a853775af263936c0c7218983b23f0187c3edad9f0457e9be47756c8554d17cf040d6eb46b45dac9f77decae6470478a8f6c0539675ff0cc0603ae4d717aba0ff8065131b67a57d09e6045466d65df0ff958abf8a3008e1e076a0ed2fe516308bc6dd2dfe4023e0a5b0f711de372aa32aa248f89266aa2fd8d21c17fd7502b533368da372d0d73c1767c9b3701138da9452e1b3afc1254eba80756c7642324036217412db0686d3b98494d7d210075e0c76954f6204b8775e62384141666e5bcd44c83a88f3c1dd8f62f9726f637a580573e3d69826df14e576c6b46c10c384eb04da9e88976f34ae54ace8d2354c5252111c735012df06836519b37c430c86bed2916bb0f1e4eff040426257b429d25752acbf4254aba253b1acdcbdf5a2e71803acf049e306230be480ebcd628ed33d70a72e71e47ff74ce5569d7334dd7f2721257f0e41d014aee7104ff0a09ed7b6b5525802c6432d7745980b4d31dd23dce1a33d4713bf9fec1446f622964035aca0b3755df41aed07738cbc47d4faac8d87a1c9e900ade39f830e46b7755e42fb96f55d2625b3646c0701af5940757816b6a88a695094605533a8343a72864742524ccc8f924b4cb60294e6aae147e38971c67b2a458e5d50f5e3aad4a3058bf585097eb76050353004d7ba48d4841f3641411d26965858d7e4594776179f7af292a91f416079ff60b1627a8397c3954ea740e40a31bebdc6d4fe6f95d701e10ad5769313917a9b9ef410fe308612cdf0d6f1fa8983e99049e6d347cac27b1b51224b982050a007a3e615b315866dc894019f7488d590211973300a7e91b99fd12495e377c106710831912b906396230e438a0e03d1e725b5363e031042358e4db6e165997037c59576f062a8e020d298279c495315e34988d5db5e3122084330115a4b7b4525069502c86d507337074623c13da930c5e51f80febd74e0f6bbca852fa14ff24caa65d4b67b0b86559c5e37d5919d069f129f33eaf17df493032915176772544214c76488c655c3e57ef3a714597e30405e07201b68dd038cb682c5654b68c730d6918758ce4da4cf3a4e40774df2b377d5dc6309b1ca55dc278c025192b8e34f96f1d3ed770ce6e6221c97a1fec783832b8c43064c33d6fd1eac42a3048cc55aef6f72632b8c43064c33d6fd1eac42a3048cc55aef6f72632b8c43064c33d6fd1eac42a3048cc55aef6f72632b8c43064c33d6fd1eac42a3048cc55aef6f72632b8c43064c33d6fd1eac42a3048cc55aef6f72655acc8330aa81a4d7050d406ffcc2f6c19d4ad601fd345766d29a81d3a36bd631e04a243dc737d074c6b562f205f062bb4b53b0294fbaf274c5cab07489761412f32c7236a50d95e86bb774ab752f4020e41703f01f23f26ee464766b352093fd8309d24a9934502587db46585ac147781855f2f845dd6223e7da0264741491708362867d43600345e90dc3aacb7304cd8640e2870e11f7bdd54537d2329c6180083ca64b95d282cb1ef5c01c485c35c1174b3116dbdde4422363a79ad1f652123bdb86ed2df602cf402a06a8fc83060621819167988d166247a383b6c01df02c489583b10afb76b41f0e27e0a091c4089fbe73ec2235c7623dac6708d63366b9915223c6c01a13ea360007268acf83600b81009795599435d7c4d482e95095d39935468743d4745fdb46e344f253a7cd72e795e09c3c212a8465b4815fb3a08afc43e0ca3101e026869382937ed586942e6e632f0be6411445da96f67f87107955de36bef61306860c533645a628e540eb43d56d435ff085ca49a386c1c816eb0100a45e78cc438341983641916ac7825658928437ab30b86ae8c051a6e033f5fb19921ac0dc6692b2dc77932a81744d90da409a34e332e84c17c201ba88c1fefcdac35e133cb7269501d044e13a65be22bab5435deea001cb5ec7b5170620003ed38603522d434aac15c31aff20c208e309031f4eeaa00d251ee2f73f7037b0c4f4f46e19a411ee4eeaa10d251ee2f73f7037b0c4f4f46e19a411ed4eeaa20d251ee2f73f7037b0c4f4f46e19a411e3fd2212c62f5304fb701092a54d6c462b77bd3672fd2213c62f5304fb701092a54d6c462b77bd36763a2b03bab187323511cd545400194679084d60715fc8f5d6b73ba4ea27b42170a9d6309c8d3d91b50be6c5d9cf38234456a460b182f733aed868f7cb05e384c30277a7625e71d44c15c6a4bd25d772d35c2cb493258e92a754831088d5fd90cfae1322d985d86043504cf1519cdc16935ca7834eddf28637d369f670fb685788474f6474f47bd532d02f9214ad7df17dafc4446ba26a330cffcea38d477c548a9d5d7544e7d5461374708740c036974b4d3f269fc228868c5d0a76ff34da86c63899a7056be7a78a9c318073908867df141853e40103e0e424da93ca88bd5610e58512891527f72fdf17b5f3d7b26727b8ee5250b72555b91bdf63118456d0dc792706ffe3f645f4df36a21b039e666adb0032fd02c9370c5d56424f1eb8b7c8774bb2e83b3e72163c38277bde4a174c6e0aa4078dda14f86bf82269058fb459105dd795b8f7215eb4a3f7075ef20684552a3758e8f132b7b178b7a1ecfde3c17d6c17c4bd4d611c3ce9849ec3ef403e2752757664803429facbb6db78fec75e5032b7d3237276f22ff5a4d345ba045916c333018c24517ecd5ee58e9c72c302e95916c4be0302d78114e430ecb077c65dde06ae6dccd1e0ce5205c511147390e037d302f42cb507c721850411d0b517b1dc5139957ed50ede9a90dffddce5166dc0b47471fcc543a2cb82019a22a7014fdae1834d93e75e91d4b27b062096617820e6e22f467623fb7d65c5c58353eb9815c1467f3f64ded3c8e5fb5dffe6eda6ea44d8d1f7a09efae7a048ac5aa0676d3400beb4cb448a07e9e348e875030f4d71d0b601f7e366e9c0c3815e56e1defce400da85a2f665500523451f66034814d3a21d4d7f01aa5381d616108a10b66b5e853cb386a680ceb4c2538d33408f56a6e3910f99b616b8a5a152a97d44e1b7c692047eaf018b199423416721f3bb81e8761c51fad4358797205b0b9fb559bf25e30e40602021e42097595ce1a32e99d251c2b00e4584abc2f440c2f0b12df312b79f5c8e0269206025438c8a02cd9b78e34d70ca532ca2f4b74f459fa754882d20d05bf3a604b9f1c5235951568864fb2551d2ce30846815e72753b7f5e699ef4206eaab80fb7661f6c038ed460df7b2b0d09d46840d20ba3505967be376241c804b238b76f0dfc473318d6d33881c3e4330f027e37b63839723bed42459f16181f3d1b6d39bfc57b0cce364e7c4f115d6f62e46628d7e37f001220884a385fb15b854139110532703c9a39f02625443445aeb73d39e8499708df7fbd44d24fc13bd34b5061a900b33a5213db3e96045d42acacf5336e93ef2acf23c52e11fbab5af2f90f764b3da40a698e1c5050dd1b285ca89e14cd72fb60f0905459a3c1433fbac4cf44836fd36312a43402dbd0a630e4f6c11917afde26bc44443dceda5023d889430438aef7369297a26bd46adf11a21733623908cb2071f130798c7d863a4d6d0b7d5024922c4c9a2a4b4258be5abd2687479e8abf5273e84d3e1689a65c3c12073154351b5fc8762534ca6a5b35fd58455e7e9b4a2ab05605241616d50d60922c6fc5e77571fd9b477962125a2931b36976b7e86772cbddc73fda566361d712711712d5a76e33b1fd23a9e5d51791f01d1474fb3a5551e2d97461d5575cc659a943c10a4072071d12625c3892754900726720118d4056988d0dd8c4be10db889f2cb1fa613aec6aad727b89715776062156bc4e625652ea9011d08f96037e5f132c1a582a4ae0d41b7d5c3236021c610662e2a39200c968212674e9a12946f1706f86faf273b30f776815970a183e75dc45b5afbe6449c7cf145cf1a84134eb8f4525a3852bbe420d2432e31b013eb94829b59b8e4679625c3d9641f72dbb981f6e98e06b24d5f9726e795c6408de921144461d3e188b79e263a900997a95b15d1bb8bc9149703c6633e564be58ead63b7833accc008a78e142dda2ab38726f340258e5ac791c3b5104c7938e661fd3b92db93152466c331a18c3b8ae4c678757481808c4635839d73cafa59411ee0edd00d329586d6dc2625c61b89a5f47d4ca0f02fad40f58121d12dada0e66976cb84858a48068711720042fa09c71c22bf90a5c789819e5cbb363dcf5de320a08003a4925ab5ad5a2904c7267f4532b6e7d26f2070258c1972675fdd7d95ab5ad2d15549776087872632a3dace1219824b54142830a5194b1d50fa087733343de6d7efc60974ba125da3f4a6adc5323662a264fe96a63c9bc116e347df606c3bea078bd765126e0f11737ed75c62a8a0ece565e0bb729fae8b610c68f2d2e147f872f4e49323fd2379c5940f62574872d127221a34c6b1fb38e113271aa4693bdf02bceecd0546d7a9854a7aa073f5e46d7020a82037cd394d902d9c4166e6b694a5823adb7612da5bb2d5db03a6873e5201cacf4f26d62b71c52b726910e772add0ccfc2256bb2413c7e12cc2f7d1918067dd4f4fb7e7df1d3678d408835f54e4d74808b3c0c75f1ee73cd18da151bdf893c17ec0d2447a5d13ec471ae77d9823c35cb347479218e0256f1efc36a8b00d601b4f1634cb0bbef65022d8023ef188f07fed695606bbb820f275dd13fa144397baa183e6dc4d3b542b5e0512546674111fbac936db9aaeb52ec052e09bc69742447fc46697f9574570614e209e0cc76435d288473051129124468f26863bf2539c4472d682995467ade81523ff36772109668d123a505b55764766c3942515460d281c4281dedc937beea923382e3445f02f500765fe32a02e32309489ed0a928c1216d26e9bf937578c2f26cdece922a7b7bb84b702fdd6455460e575c104a7c0b3af66d5877b314371a92191fc22674ce32fc4a6449690fb96d373c0b499974a5d4300f0915f259e3d5a81e4da5950a0bd885723ad21217035e8f52861ac258b4bf4d2d381767781dfd0e1cc9b18b64b6f8e37c9c03846e36b9ef6b5a2fe549a8d6cf0cb1051971dd1cb813b5f7d012a738265ce216c80d8625b31a2af07a12aed1cc4c820ecc179810871f8ac81b076d36d365a7066f6ef1268d184959cc76aa15cf4ef6adeb180681203061988807d9b0b626867a0f6801d0ba5a77185c1e08849e3ea90cd9378096c4234364f11b00490d43aabfa050d53aca14e3deea61900a95202156e222d9c55117cd0fb22ed3140b0ba73e1b676da515335888ba2a6c368c7ab33d1f1d0ee2c304bf333f3af491811673c0cb4e9f2fb65ebd27da3cb18c261312eb0e18c3c6e77404025412ab64e94790edae725b27bb341c166b57a211ef3b53311a433e6a8d3ec56727079bc122153dc2a710bf36e5760b1f566115957405ab3081298b348e703fade65fb5b7e839bd5c46324a10c7448370232868c7c64ba1d8ee54c003f75cb32bca76121fd14bb856407d018a80677d087052e224b55342ba61297cbf3b6ef9c73b1794cc715836f6097dfa0d7c4187d5634d1bfb8d5ea44d222ccb41a1375ae22453f144102e92b0e51436e7dc3ee535b21851d2be0d353bfb2183e3aa3d326dcb59587333130b6e4b59fc754318a6aea4644690917ad690701fb1c7c04b786a0c4f63b51d018a91ba687b5fbb72d2e0fe0eac6ef31ae3ee0e3f168ad27977bf22371a391c1d24e7c57a2839cf1041facf639f58c765086aec655ad96515dc12616f629537321686f02d63df38142846c731a9437d59e6956062bb8846760e158244b08b151c4604904f5ba485628e2a39716e74682ff6a647146993073edd5a0d2b1224323bcad20c01d643766ccad15451e5dbd0135d50191575358a22def97d3464bf2f0935c11d64f888c215b1f80348398417215447d119803a09714c08fb711c8c7058cbb8e07beaeaca1b1769311b1f4e417df736255af1a9cb46983fbc047e19e034a718eb7e04c88b0e7dbc565ad027f63954bb3e66a6697443e55c40201b312949a164230fe9854a3ac1dd2d0a2ef0de78d47e877d953a7e15acd03f33b5a84a0a81aaa7118c7a9b086dcd5474b25ca509610ada5418e43e6f7cde4712cfe4a6799f0068708b7c507784acbc7d30056226ac7e983b52cbf23580e9d27b23601207ae3e8b3b139ed7787644941e20a2e014698e0d7edd129e0c0f05d445fed6ca77a9be7e1ed08e811692510160484d6e14b1c91430e298702cc28d1e03ca4d46141b6a4a7ef511d7737550ea16c59e8b780be4cc78d3dc4d4a45d42405031fbf65e4c39b33535eed5910b2a522a95425215935af079f553d0e84be403aade81c08bd37a972c372b064326a366f4b6caf58487b067a8bb9de0f587e7636fe1096072aa93a3793eee12a55d5e913711e00641e7c804ec99f36581b93c447f0c9b166ee120f5925e54a56ee65873c6b9461706ca84236e999b66a99c416723671d0054263007ce6fb8f065ab85e36eb25652e329ac90f971a1026c26c91568aeb9f32dd9f9d30f2de7f1c8d18e55cc265a641b7fc7b7c02d475432a84f16e1603761730876c4bc52180635dd6252f094e424d896fc06d7b2d0b3c3836cd1fb35a6a013e94170cd5f7c220c51999465194013be19cb5793665ea30880b9511492492229225b40d5ff5776beb97935caf9a4e65b58cb451c3bfd257271aa03db08faf71aeb3fc0cb375ed6575d73216165faf21ebe470328e49b1237b48dd488e477f651f4642192d2ae85ae06ff8469e27d44ea0d3385d6da90e2c13d44749bb5808004edcee2c1389663186f7931473152864a295035cc54fcd6f07845e065bb73b60ae0d30586189c637f7a5eb64de85651c44f042545c94ca72a7c1e031cc634e40e5af18188720eb0cbde7903c0b555701d954f712f6104f0afb89c166a6e867138ff70d5e98e93e62d0cbd72b53af3c2dd2e8f53cb50e1444f0b21f58693f5f000ecceb7a26f36f28d27dac19ead85d32ad17da2766c8db08a578d30c65db743b952677156855c543256a367231ab3c4f994a4426041e456a3c7f8e08cda9ce0a7fc95539c26c182c15dfa94d88b95a28e9466a2b37d30d55d5598f16d3c0cf70f7aa097ceb4b9871103c480789aec0245fe0d53cecf03617b5e8ae5e388480392209723e243d3124c683e147d46e885e213e0538f919113916d9dc37b604d06de4014247ba70806016267b1d23a64059d20dbc5d1f1620629fa5734e91a5f8388d7ac71d8012d738817b9725c5a6875176ad117277ea6562c4f6cf1b04b0cb416e821b13f0ed1f761c8b6e59fafffe6e3ab89427675c2003dcf4ba07b60d87678caea86ecc720d7c9550df31d9ff125e60a093003f0d16316f9440061f4e690c8c11483c6cd4c73a4977a65d21a3d97effb68b4f3c03fa27d0d5a6081e4f1124ae3d204302126a30e9f5411e5ca6351c4abf7b7072ed553d2a89ef04807c151142f9e13e365ed0540a781f0e8f4ebd707566c7194026354ef7333c40996e692188f9831f3210037a7e7fd5102df5cc773d3e8a473b5263221fa3570bc090df596325146f97c88c29aa0714270078a211f711e355e32a68228e2cdd10598d11104f257c3e2b9b0f61f1af7507a6fed5262fe088233a91527709c47d6617b45d7e7278a2708bb5cc4f3dfbf4161562a60c632cf40ea259fb528bff8627721b087e75fb1836065ff57050d0ef78bd85cf3792b57d20b10a6c7e51106d461fbf5b1ba268dc53cb81ba0a4695bb2363ded71715bf214714a5ec028b7f42235d7b4219009a3e7bb7ec8e4daa1e737b2721631daea5873a4057071521423b0b389af30486c91d104682bf53009b8d0190dba03ee6d6f233cb8b8f090584857c58585b13309c5871bce45f464ceabb4d9e16e80e74f0fb3d78342108f898f52190ada848e4048c57e0a6bb4cb65b373c64020e01aae18302463e4f78a0e4f85c16097d3e7ebb0d7b2f9b640bca037917a09bc2166e535a60c4ec5004ab118609529e0c6bcb7e2d749ae6cb64bccd9078ef385f3966d1791e32ce127dd01d2b30d7d87374c0b79f55590c5e1a8f0c30574f708558fa515a3ce09624677473827b4d6f1a298e78b9544400fc5d6a84d61823bff472a3c2856d92c6c74a293f9f605cc5a019df70f667e25deb7c9ab5a01b87e47b48b775802e4adfd8131b124f6b81dd4a48a4284d7b5f071d792689ab379104526207cd0d701b05cd62d3a0994269a7f451188cf8364038a906a6a3704632cd48756d04994afa235d1aa974ca464a434905e164bb1351d5af2dc92c470c55a28064aa700c4e015cd22a400d696b4a1c9c1531ea231ad1417831b87688796416490a2cab2210182a2a5f88c8db616158b6098f70a70abfc9f22db81b2b750a4b290a735efe2038f1d70ee9deeb3f8e50304b9822522a3973cb348ed186451c20c27733fd064d9b816628d777e45cc256e428cd8d0e4274f0d010148eb82ea594366b480b72653419b75243eff054fd5a526e77ab826568ff5b50d7468570be594165a18569694cd0e243867c451d1d9c06773515956358f04c1ac63d496c3e9f2f022f0fa606fb852109a36d1b6929585446007d3e2f7d2de23150809c26e3f4da7d83cc46003d9ad316b1012f653afa6f6ccd96c844236e9353aca4eb3050f90a261235bf0e18396267920ab260020535760d369c3623619276c1213c5b4aff76649292ff2a4deb9419dd176a30515e550574d59a4bcbd9a43eec4413002918373b38ca7326be995d7d2a13d23a14967b14e7894b3765cde70d900fb356b5bffe737cf1271e051a0434f58b1e491396595be1b84e6a0d818f619b41fa2f14218d110a66800d5422306c5f8bc71c2e8da774b9c28c79c9c22b0d8aaf070642833d23dd1c0d59cc3f0958dc4e917835ac7229e49811691676cc19c4bcba7b9c36355ed835cc0aa2dce05613440a3c54f7eb471bdc5e6e9926fe17ce0a596cf060e86ff88523012c2c17307bd1c36e1f63dd4fbfa0b04f0c78477d0901b63b1604555de22d040f4a5cdd4a3d419e24f0009b1be11ac831d7778351aba4fc47f13f1d7934c2e2301f865e43c6edf360071e9d5f971ef459e190eb0488005d068a76f143af7d7e40cb02ab5e97fbac109306c5344d0d8c1f3a21cb23549c5602e4fba16a1eb98b09db10b25fc89626458e50b100bd10d92ad616902f16942630ddd2d8523e111c19c8c6cf478317483bc9703522bf4a6527f6e3655f5f05236e9ec0604cb288b000a42f8b4d5f9af76edc0dee0dd47d322c5f5f0f6f5b04ff23729d354ff4b7483f70cfab383a759c1ec49f15079f2687635aad537d64a8e4316b8ba02e50efe740fadc9d433f45e009ba62f62e32aa9570a340ad3699be713172750f769d1a97163ca7d32f6dfb10167617841544030d4715e49c5569b928721180a27b8bc0411d87cabc4de554a80936f72649138f4f6b251779399afa7324b54c6f78500a01152ad65e50f372df5608e56a2ef39b9379f344dd7caae40e5a1238636ed899bd225ff2525f4ecd0c40b0ba667022b9ae47115bcb6aca12461e297fe60002e3ea46392ec2208bc1e4171f3a542dd86ebd10e967aa3823c2e037f1ac0a76ea2e325c991622551e357f7cd176fe3707a872381cf56756f06aae608e87cb6e79dc2352d29b616a5dfa931f7515730f8d0197560e47f33bc357a954496f64717adcaa463c87c7155319fb15512cd16e331f9f6259fc2b3c9300cf1d30582b5521014b0ee7e1274451cb971360f7bb52bcceaf417f396b6d175adb5fc76a290dac510e116d161727480acd17069d3754f65fc04fea46884d18d3df38fadd863203f0e4045b68a93419035463782ed46065f4fb32c2c0d940b015fd5713375c7165ce274ef703c5268ad52c5dad72d575d0e06e62fde25526c89f6b3aa4c0286b2799123b9de1215b3d08cf7d129c9e7268bb3167c7f56f32a807011055942d4b64e9302c74e7101c7295aa7b0be3c25f891e4763ea06553c9cfcdf0905182b06dc71567d61cffb3900d89577c3a6a02a14b127774c9c375a5931435430f1cb27accb542758d9d117a8c938165d1bab5153993605552c3c26ca001b606e4e925415c0087ea211925d4bff4e6ce5a5db0593ec611ca9850911460f221d762bb3387d87891c1a741c5098d295395b2a6b293007c4565cbfc518172322315ce7845b0599fd0a4d0be12160dbc925f929e805ac01b9576b39ad0e6f8c777c808c4159f5e7c33c6d9acf26da40c76bdcab5f4aa56cb03365667d0972fb642332c0ad069d714627410399332fe6a3798d6fbc52cbc6e03b27bb257ed4d9dd12cc5a702b0efaa51f4973e176eefad63eb5333526ca1e725560e5174c41003c6763ea202e6cd66b271a7fbf760908826dcdd5ba3dc029d75f05eb9f74c4b42e379796734e524e9c4fc7f09b2b5309f550aa5f04689e3be02a257b2c093ea8705563051d09994eaa7544aef450cdfe5a6fc756ff078d5fed6c3220842eb5d7da7d0ceff4221d4373206db33460d5f97e2ee0c7355c0903e1216a49074b6798ae562a6e154e383bea1af8dd0314ece3a766b46f966213ac21266b2b206781871d5588d45d6b81225b15e226f07d491f384e6d511b191fa2eb40e257bb479eedc23580a56e5ead0fc13556708d67be7d771fea36183a60974051102c7a742690937e9ed8cd5905bab23ac709870574e6e6269f3ded4c0a9a4111382dee3f5bdbeb4b1ec1cc031a6cdb016256ed4570899c6be0cc4119db971d11e72e650c7d639515cbd402604574581754117136d900534f4587f53707273663faf5844334780f64330ac2530ded4b518e745c5add0c1371871ac142cdd0f340655a60117b8efe1be7adbd2153b2d53656815369f9df1f114eb95f476fdb8c1cf9b10100e24a707d757a21596f56d771f660c3710aa91a6de467214b15a7705fb29afe6a923f594c3af5ef14a648d514e8dbfc56b3738475af6c965b5e494c0df311157a8719781efb32532c1bdb8704f604521fdf578e12dddc4f6f6244a87242783b56e3e46566ac250c307e5b2731a7eb6315a5f23c66639f1651399aab7b4dbc766ee103fc37801863026a83a2537dc3e5041ceeab4ae2f28e0f1dc6ee2492c281223eb9a15c01ae020e6fd9272df15a6371e1c3826066e8774056ec8e622b9dfc6517414042becad46e687cc541f38a057aa57d5c44cc72c91b832da8348232255ddc1cbe4e84fb7f2eb2970743715ad211f6d8064672055d0e2a02c54d2998ea0a3d66213364cdf6580b40224d94139a1c58e0e845dcd9c911e0e1c3730e87fc105029710eff40e2433ea51718c492577b0628314fbc3b317defe6462ac7b83678554e33512548321179f2cd1dee2a9767c603b1260e2d7a0d744188446d371f4d56491a4971f1a03fc0ecca4d58b86b44721a3b4a4f21860c8a3bc324d201af1cdbdc750637d10315d8e8814cedbdbb71c7f6916601121b1dc2845233bd550755cd21de4700f3936d0263b00caf5e0b4e9c14022c679ccc14fa88cd5417e7de6d92445651aecec50c4c96602ae2562800b7eca377d53e3f74f25a2404aba69f59889201375de9e76cf3534a39c69bcb1fda303f1797a45b2e62fb6053379b5b56daa5e85d8a02e07ca47d880962494e03142af4410d29a54507f7db06e706f40db657b96c9b086858570fe4361421482bf2241c72a76da45de8fd5e29d73d2709c713b92b5655a1053a4db04319d3ab43a9dcf65be65d5d7354a1520e71c31b13246e7e2475b6e9711de0a45e14f6d10280b3f83576fb0a07b2db6c36024fb5746504c263d2f42a53b3ba975db29b5b011dc69e209f747156855b526f37d84c5e1f7cfd6600f0011bb093446284878204b7dfcb7eaaa8785af05e3a11b1f6bd74bc8b3878c35f435d8780fb15e7ceb756a601095c4048fa276b24232a637f591ed229c8341fde8c50f53371114f19512375e25937b8c57d45eedd73302513707b58ef68630e40723a89aca54b0df385674ebd8f77e94ca3369f7e2236c29c742e61732829f7f8275d5290c31949cd3c3c07623305e7529a7d62629f1964cde6400cf2446581e10a345005ae28aec6e871a5b9ac290b47ed5348fb775c627ada647f044c7d841c6e6e51281177af36dc7a25a371018766df47e0a89b759a99f8424dba654ddefc895764103e6e3640080fd2cf9e244dff1810597c7a263a26a261abb6f718218e012c8cf2fc4f3c2da54c1b5db35e92a47968dedf2d1246b5dd0154c3716ea6669f6e53026c128de11f3f990fb54362d42e234dc4015fdb57aa21cdd8e8270718c92a1b99784df29b706441e43635a55ad1724a30b843f8ecda7bfe76bd1ce2fc580048e4cc1d7c70f52d0404a536c798e14b4036fa399948315e5fc8024dc930867d75142f7b712ff56dbadc4a2c41845c2120e7a90a63b3ff3c9324b2798138b51fc6b5cf2a76470c583f6420362dd2166e3af2900b44fe350eb47e9c4915888f3715387e25f010ca545b3b985818e2984f0421a06c38311b6a11ec73080e09414576bb1a51bf850b55f439e12aa9a9e7666273f44a4c01344e9f4be442e59cfc11f5155f3de3ed2340b8767e4bbe6ff74becf2c90b0fdfc33cf19dd32562a39e748e0b414b8c97585a047ca3490e8e310a9076574e4b754e070b16f4153a943029ecc6707990734527fa91cb36ba25192ae8f02e534960b55b63dec739c63ca1286b81c50800bd984251754a45cdb6fd56a9d8257775c4ae6fc9f1736c8b8d443dc753a427af72e41fda7ae53c3257541cf385f4580bbb066a80d37f466e477c14cc5c401383b7a4622e7aff2745141667c9c96277f70b42185b7025675ec23336507abd666a83912ab6b69c5908ce2f4f2db07e7350aed639dac46a20da925524aac55c0ebe37b731a82ff0506dcead4098b2ec7b2d738a292606010784afb40d8ce0f8721d42fe0fe8ca4e7885e0ab26e3f5e552485f5a05f2d127192fc27345a1ad746619751029f11f6614e94f0478caff825926de1e407574d31e0cb3b0521ec952639668d23355e59600d4397c257e0efc28cfe74b6cfc6391009765967146b8fc67929da87ae733fa050da8bf0ef9f72a74e9f65d34faa0946d3a3fe232b525015bb33a317318b574358e1b927de1cb043e5e15290a4e9c027417844b235ea83056bc7a0358936a3b0f36c3c461d964b232d428e766c9c47d4043790e31cd31c2716a322f748523af3f90f32849b9150d0b0aafd66c749e3168c1ffc768d640ad1082eca562954c7f29835feb30aab6b312da26611110d2903f478dea02d40d420ed7b62e043b50af2ec6e64755348baf4b618c8f3166066726e440973407862d149fd2ec01f9f0fc2068a78d54b957f47e8f4ea563ff90bc3f4a745a2bfd1f0b5542f27201e130f25468f0b34d31ec930bed9c4864461fe570c1a12975fd6b122c32d7ed76418dac07cef34706bff9fd7cf2eb710844c189799e8eb1788879fa0a201e414a86dc917829a2da7c1d07573305fc5d588c59c06644e5af7819d9715f73cf1f7e875c87798fd6087b46a3370026e3754043b02d086f07f3707e66775a2c786b524026ac27b717647b78031b69788a706dfce87127d7e85b6db6a6654c8d9d0d78b9d35d3fe6d5a627ceee1e4be670cc0e490d4c6e30b13238c1c45c7b351b9d3db77462524cdf103370e49d78d0c2e63dfa5cfd540bdbc5708009d464a7bdd912826d9b39bbc09468c4f4ff5a19736152f920a669255edf19d49674080126281d73c758194860d071eea4c601d0cd714bd637e542c7917518145f646fd6690866d83a8c723634476afcb6d000418af6115b8e634ee857447bbcdd843305fa6364b854b73edd944800c0464661baa6b47e4b44ea4c8a0eb50b51d61c0e3e09c62ca8dcb7625ae81e73473c7b159986465af787a3342b1e5218d5870c4d6be1f86da1d16b3f5b6a1574fa425c4aceb23e362f73b06d4f03303be08a92453db360020c968e3782bd81611035681a7ce66c35fe03090c713692284a86e430c93e68577289186bd7642351cf24725cb9e68b721fe9464e7b72eb575387ef5b0a512e78faacd04db8b6782245f39f59edcb6f336801014325079b2308893667e1c5df36d31e73645eee6d372c3e91027d80b23674c3071cec29eb1cfaf3601a4bcbf96d05cf602f551af750569e72624aeedb5e4adf66494a07392280422a7d4a2b87575779e5713b74aa45905d603aa712f23f2dc77f28a25a3c6f8d7f9b268b96120502a3c11a8cf3653574d791137d9acb7e6a7f512bcfe13169a8d9c602b98da1215d98596e5fe26908f32691751dbc2a6018bb5a7372219c1e94fdbf7384e3543d10874348c48dad384af7e676a086a115ea304250d43791290a35e916cab21a23239fe3421313c35490c9dc1cc315d013539bf02ad0920b7b7b2bc90e9fb07048c98a8538c68d832dd51776215f0fdb254f685236f61a7c2fa253161ac3373118220d94176794050917185279702b5b20f49bd2215c48943d57ef6c5e102a9b3fecaa687d3efd66103e37f96343ab762da28703014bb1773d0910f22223f8af2132a34862711bc73f657a6172dad5e37424753116fa4c380ea3730c0b03af2930cd7d4a1d1a0e392b9ad03002070dd21b58405a33af017b4c3f24ee3498a11425ce19f258233da41e8dc9525ebc02f42d42a99f58ef90a56f9c9ec422fa512771c09402607dbd302237652d7183abdc5ecd66a2783ae8ad5c64ea8912b0441f2355765f1e018f91632db3457821f3ed20a625930494458c75a26d9756994d883a6712a82b56d7702a4c4b8b5b0d508d2707db0a456315462bdf231170bd61ad636fade62c192ea365655696615787267ae0fc110abe97ad6863890b728b13c35c7135e24fad10d55f1bbf1a4fe8613b670d135b68deffee1ca5e9934c4a3b7437b4687061fa132e7b2ce3ff23348d0e3320f32f289fb6533bab7e8b1ebc78445ba4b1b507ce485e1ee643ef39b8008d76a716d82f7c0ca946f2b5df0f04e06b435de9da516d6909113d6724389ccb230393b9315eac0b20045a3b3a6eb355691337c0906c027343674f62cf11ea38d2066b20ab02dc37da1405ca317db60aef2dedc2886834dccc08db72973d3b0f7f323b9c1328652ba71aec3c0a7772c6222e6ae6dc3f007cf92a7a3c5429ea1f485f360eb5097b019c6c64d21116984bb5373facf73acdcf6835da3a9f7339c34f75dd9ff966e1bd9541bf0cd32652512f7b448ed22dd0cbbb4560498b51c79d5659fcbcc04cea133b1cacc6a60f650f4e002d55e3261eec7c488f9fc9258d7f8b6e1888202e66caba092664ad69d0b7b924791c6b469342b7566044a0013ac4df163732ad759dc9866347622a3d0e35e50a69bfba12f488e06f0318316f0c52634bf9159129e71ee0047792a734994d86661a4d1c4938b5fe785932ab0ec128931354ee471b8e829c3783a0a91df1db76162907cc51eb607e68c30e3f3b9ad9f823eeb79473b6844a69cc6a3739fab7fd3fcdd33355854f351449595c7d83c192168992d1415bd9665f5d008e1b95a92839f1bc16717349cb0b7506fc095abc3170f821c321f3987e479cc51e7c9237ee675aba900d7037e850f19fe138e7a326674e9a227455b2567be236d6744b96cd74ec01e832ca63f6131435f77234d7ee168764d218a7ea1b57d9705d4166f020430ea97b3679c8c9538335ce4cc9e0914b7076b02136984d3baed5367e8f017b719ba7bb36ee0b1d642e3b1e6332dd6e5ae9e4be5d1eb2754b7c1ac51d41ba44081bf1c620e2b6bf23cc1b401365216922cdbbc917f8cac608c1a9594df4fb506665e1486fa67d5e251a113d09b43e025a06c0d13692757879c260a0189cad9e3b425ecc341f893f004d4f2a2b09b0eb6142b22a0e25ea545f73f58a24c4a51d2f7dad5f259c6cac786d5e08164f54e521eae5121e83c5f366205cb87c91abe9181c73821562f8c30776b4de2013e7114b57548110e207e12c6c401e774115d0448d7ec251b767bd56c020571c9f6b7b4773d35f1e83be5d7359ef9e6ff6ee790b2c7f886600c38414c654d92be8899078fc47420bedcb55162e8ab765f95ecf78cf009041f7c69f2cb8041d32ad37ff3601bd9b48e694f772ddb5ac4c7146b34247352c137c78877e69459638855afd438acee54801c87837f0b06673991d2535337dec42722b097672b047199b085c0bf6ec5c72724f0440c77ec5047f013c5823c6871234aa9733ee9aa7062ca82c08bc59c92caf4d5215c5f52b60b23e540a99caf026e984de22a755b61c234ba0646035087ab03f3f7c7fc34454d1744342d998551c8e6a7b5a71a1f46b36c5e1190e8eb647fd07a47a813bac33db89700e85835625c2c3ce5692eb933ba971fb2b6184fd4b3443015e789fec6c1f3f482d69e03c7ac917795d912bd5652bdb431ff4580070c79d35550150762cdee1e3245c613e6f248b753dcc7efd3548ed1724fad791363eebcb2de841a40bc9cae856a820d7351d202c59b28cac735416bc7503291138c1d2cc5ab2af3055bcc8ac32138bad08c1679454b07f640a6a013c58802e7419f798411ae1899715f6f7e61a5debdd3dfe3e1321b56e380a16b2b43e65c71207479de70b6627d3007c587263f96e6953637bea099e7f5a478775361a70260c13c3f8280e434ae76038a5aa28811e61726cc4474cd15e2c62da6fd54746a78a0a7b5da74c81449b61475165473b3ee8499eebc46501503d452e7ed6345e9d5b0f23ec6f7a518de97223a4774e9a7f8d44df6e84668573596a0be53119f1e86450b9fa2622f4e9c27b1a3e2265aa326c390c24531446648275d08afd381b0dc52c8e7043186653b77a65322328efdef3502cb9c106640c6638aea3446ae6e529527c12a4345f1447782ae3ac1bc93d852dd6868117e4aa054edab12b0eb0dbf70dd160f068b2caa03d80f44c2bc8b54409bd2e8148204b90536a1adf3602c7a3463dccdd66674d014180cecd45a07ac712d44da60fc339517e7accd5126b064a0a7bd3ce3fa1894d7af6713c2f3f9d611db866ab2db92c4c43648af22bc7f9135795c0331fe045596c06d31915f4c9056e9e0f836921821c3d98d2d06a271d2071bdfac652d201094ca434176b777f4a4417f5de43308bb6211fcac614a838e876c2c6182f7600a746dfab4d61b408b5767d28af1c165eed1211277b5101534512d483354225ea204bb6c0cd5f92b4691a4fd3eb37f445563422c8e1400c2e36211b12e137b8955a147d10027d59d9e92c7afb6f62686abd4221baba7e5851f71167b90b1dd9794f06bcc180244565d041e756334bcea6315360ee89661ada3a4c4aa7d5584179a80b8dbb5a280c052971b5592f2daa46627221f073182d4ce02e8bc3af3b0f45711169d0023ff511d2176bf9c674ca9bf661c8f61422a1dfaf1f7e5000531178797303af2c344d548e5714670d65080019793e23634a011824778eebb31781dfa563e75a7a5fac59c479f1019e4bf17dac104196d64fa27e9d55ccacde468dc2776c7c23851fa87ada3257f9354bf5efff264aedc9604b185918d7b6927c3f190e5b4c4207165e76d97d3304443b52384a699004901304ca0d06a9249e38a3f822181716b17c2169bb79aeefbf44cdc90f60a1df2d225c1fb7202c5dfb4881c4c65f79cc592e4a74fe56c89f37380407bc5fa484e43a7fbb5131d2f7ad4e7fa821528e7b3c26ce2cce6d9838b00eec230e4d0037bb2ebbc169651f7d2e6d78e53312d122ce34ab38d03a9ae5c013b342460df9d8586538301b55d0041c21e24ccc64eb1ccf6a67c709249a32535363e82c29f86c5f0b2747716343ddfe345493ab459124c30588fc2d141843682570fa044e5da907208f850760b7fde07c3d69a63b9c26e348e2b93454f466f1564ff5e134ad2d3439c42ff504c356de274597be6265e0bd0cc28d1a231691d634ae23595b996a8e7c17921c1f0830dd6d0bc1a66867d8bf0bfcd46971c8e5bf281cda562ff41d563494305128cec6c86a39b7400c702133303ae0df337f217f3770793e57f3a67937caee3b2c84ba0012394cdd056433b410b2087f5f6216ec482650114b8652d47e8e310b3abfe7eb2e8b76cd5b0352a74bb8fc322f36370805ff28c553b51e0d1112da800c91aa647970225e60fa533a4dec6d1262881a3d0804f8e1651ef8401af51453180291194cd6b6e55094c3387c4a97ff4286cfdf772dc020557d2f0a49e7cfff189dfcc71baef60c0b90331c1adc901f268e7dd932dd8a8f1363b57e126bdbb830951c657a8a0ad94e0c6c03278cd3d944c5edb83fb4a4284fdcaf787e83ec0a10b7e1a5334241a910d0067a349f86e63a5f37c31f3bfa752d2ecd53500895683c2384833fb71cf55c85548149ba475d30ef395b2771deaa43445ae165c3fca47e5a34135ecf14df1426744a5cbeb4ca74beb45d792971b059cb9da91017cab65da89ee341cc5a2950bea0525a8a20b16032edfe1d7d737b15bafe354c91e76968a35e0d0f44969d6f69e85c099cf570183e2abe4c84b6aa53acede63701945a6ae1ade328a963af1d7e916a5d21ffd9775031f72a4cc5024e71f0a144d3d19c264ab25e3e1dca6e7e1740a136476532698de3db0fa4d0b91c10a1613e955322019d1f6825290f5735e207dc6ded673b4bfa3e9942bbbe4d7db5a02c2ca5989b1009a56902f799fd6c9be29e46ac98b33545990c69e76f6d061f912902ec664d580057266fb210411b06997c677c8ef079d8d9291dfb524141829b5c390a6dbf663f927510d3af332baed567684f62485706cd45224736dc2c6672184d3245654f65a8d15e61679071077fcb526f4c3236a95b586bff87c50129a60322f358ff678601362b9e5ec906e3430e2d52e74d57f7e5c31a0875ad552dd4363d48020f43581858589792a90f500c3a03783ec1029385ab4f14982c1fc9911e6f8a914d7349b9b70726639a397ce03d79cb6f4f2df7862468728adf51dd549b5ada449b3397b37906f3823939c565d84e19eda770d6b76d283fe6527b14e6996276ffa276b7890b29ba7e0918301b6634670d4f6ae9b0e847e0edd8748ea6114b9b0d4529a36dce05f9124924267b9e0217b16b6baaff9938ebafae50a4817b14d8c24901eab21833379c2763219066309ee3c618be055e4fe8e26f5d53b9593cae8ce42fb4ef403660a8a10e90a70b07ba4bac5e111bfc32b6b8ea46da0bf736739f3d5dca879333b13be015fe539a4a4368bf3c7ae5167074a88a2f69eb01491fb1e749e54bc51fe1662a6bcb15cc4a63974b2158108323343386530b3b1b1ca864b809e5ccda7e4f14ab42c0577403a647ea4b55568a7e6061bc04cd66d027e25d083000a9652158d9873210c2fb73a5a3512ca077460a5a6b69570007e04921dbaf3265a0aa54be0788542d4f1c3ccf013041971f8a291ffcea0dcd28294c7a4ea43f6c42e9289149886c4e64c12e2d417535b68848072471aa125800047ccaa7a56249d4293053f6bc312c801062fcbd192ebc90cc7a51fd9d377c842b2eeca6d44c5217fa4ab800ae323d2e7f58ac6eae4419564900e1bea27c9ae7312fcd10aa43676e575bba651a75a146236e1aaecf59b6d56728cbcb8b5308793362929e175d0312bd614b65ff4a2847d371d4230e70dd5c043b2a071e6f35f36e57f719f5319a857664a1a0cc200775504e14b8d93114d4ec3f88945270b1702966c1be4e5526f49901f49a634be23b310d2e617150d332fe14e952e75d9b4088357843606f174d6508ca834a0ad2c3de7bfa3ebf44a218b65d0f2fb640c5afaf7cdbcea81083adc47b3cdeab56519fe500cb2f4a05606df12583fdfd76790a7a34b74ceb7ee6d7fe1c998d6b737c09c9644da3b21281fa8e01adfedb5b967faa2ac227a435e4d0354024849a5b2b2b6d51843a4e59c33ac91d1f7c39209c18c6327ff4df66b7ba4e7954cf8a6f3ecf98130616c363c1f2a622b0c9747d2f0e1b64c3c8a06b63ee4a74d468606b504ee74e06edd14af34d78711397ce1cb8b53d1b638c940e64a2f1483c3f9a09dac02625e6aa70037d42873c3bd4275b4fa807145a362175939d9311fe083534c202f03226773772c5e6a34df048a10e7d61237b1bbd1056ac2a05022138a65a822dba672009b449a3ed530e479b252b38eae14187cc1f6e3c530f6099dbaa080bf9a34934faa52f1f9e730a7ec0544ce0e7d33b0926c046d6e04954a4ccd7094fe7bf295bfe4540c4ef8b339a098b1001bdc04da1b3f06965a83f013338530eeac3632eccd26d43413c72433671cd5470ae1a4659146b38ebef6042935c452a9770be17a99d9b0a698ffd1daa686d0d088cab40d3330c6b1b3821490db7492e50aa193e766d7a0422a257490d5ec328f694912d6d7bf1146577c10e3f1e875f9d21b72994c1ad1a0acf2b61e50d235cf7f5642385507a016cf18d48659705609ee6fe470a9d2a594d838456ad00a2405010ab2595f9cd0f58871151fba8350b234de6446e97a126824e8059ec48753277c1004178653a347ad5ab540afe744683598b6b3820ff7de381de22f22b1c3d6fdbcc3e65e04974ade2e64ad1abea6502cac06861ce1301853ee3504da7721369c2385512c5b059a2386c24ad51d73a87c3084d640658021d90fe686fc4e4749990d00c215b851816eb455a8d3af9781498b031017eef2673366968d2e5f86484115b5dba69634cba54197e575ce84e7159e4118f6ddb62952ca315a4b01d20d6e0636cda54ea3f1431a32a97add427e5e19e7c82ace9358cdc973d8d8198595a04b8769bda386fed16e906a4a1a80018fcb4186ca286401c592708f239fd49590c706b24be1324021e5a294b3a8805526b6217b8171844de083f26bbd6ff2542df934176f62e337bd95d331192d279f9bd300f1d306a086d190008f1d43e0927003a0b92afa24d7481a80e2b014778cb35307acb2c1232d908fe5edaf4d8710181a86cfc563901192a2836d2fd041f13bfbb4d643ca20b97609d68ab9ef928d704153fb01491739d1b8b2f62bb5479751ef4501d51716a61483837e6776d32848ff8685a15fb691f9954203a0eef287d3ef2146256c020a65f8a6004d7384d1d894e61adf9930b5070f522136ace17d0344d6e959f860a40648010aa88505dd5b98e30605dfc2b0924af20870f240c60121207e9936e0a8b2bab2e64def52101d0e67dfe1d6a5480e54a562fa5c7079defb3435e0d0c168bb2c92c99fe6d22ae37ea79550e515b560f374219193f15e344990c2fd78f51c875353c36b74a31ea00206852589513a3842347ba62d31dd6a6ad2ca51a6b6ea5819327b184ca3a0258404bedb895750379134edcdba134dea2cb026f8969531785d12f5b1e3d29735b386c32ee2e20357ded26a9f298771fd2ef7a1f67375eb638e6727463d84ebd5cc448c34dcb241f0aee4c57b3cf78dc3b7f266ba34042d5b8d67aea07a368c20dc06fa7d21a1ee2441c44d452736e5e3b8674f19f3673593e612b55a3fb29b96b4f068f92fc346675d60c03ef8b38fd2c0904e0c96347ac37fe3cae892777d5f38d5d6335d157434c164ad8bcf0477c083452fc38740119c9437c6603b9531b0d751898144a0b3968cb35208a7a56cd7abd17a1807a448ddb0a50280bc67a4353d5002ddf202bc24e2340d9f1154dbd6583092c611c0b74ce6a2e2f3ff873f1dd581b3d7cb473bb947d585bb2466ec58c0a5ef5364227e97d8c6fd9417b29a1fe834ebfa5f82363c48c43c2b1fc104a528c711624e47bd109651e6498736d0cf6ba2c203d9923bfbc1468a8d2424bc4882606cb68c32e1f703c55a3291a3414590538c1353c4cf56b3e0444bf0e087036422cca39524bf1727b6da998c75e213b250175a9461deece0979bd6f6b40247d54532610626c4c486864b7ed3563178ae36e67e6a733fb2b965652772777075a75747c3eec489c7d96058abe0e5a6d3f5e05a915a015643f10417970da505ba09718d8c9cc056f71f00cd7dfba760858c1354e25f40115d84c49552d760729e949231d15c85bb7197e450e8cc3612b5654587ba72c5a7636494de42c231ed9c8cf1726638c01d8e5ec527ce8505eba60244325463f6649952249d3fb7d790b50375e85fec06c65264f4a27a63c3f1b8c8731a4072a0429d5b866b83d51372951256265e58815e4d2136ddf03457585c66843fd9a95265c4b97655e61647c6fcead5e4014975353276a00f04c3f6ac5c791466bcb24174d43a3579379c42d6a5cd04f1e86104fa72be87a10bd5121c4c95a72b0b5dd00d0131d75c6ba6546038b3146e896c5222d508224c81956644096fd5cd9b8222b2b1858193a88467a817016000de94653a4a60b6aa4302b57c2d3662686f0e00e3bdc096eab08ad4e180f0349a94a2045fb5784759e28ec0d145bc532450d52724a0e8965c3444f6b961b6f5089ce853e512d7f67eed03a42083bc01c2c2b236821e4384f67a03b4dae5b9d7e20a19e1189079f7ee0f69e3d6908ab11fbd70f5ff783ce4ccd988646a6bfa35bcb1a1f540c07e75144d8f91bb75da81d493e0d5d957e7637d9b1a712e5cf51755cdbaa2a6e454759146f016c9e576f13bf28e124b92ac20637e371163c3f68472a178c3f1a8aad295bb404790d17b2622fc1387dbd2156484f90df2ece57a05b789f6274ac9bb611c056c56904cbbd0e7b0b4f3e34bedb181cc43c020bb81e4fa33a4e1406124624461c787835b18e5bb8e2214d35a1a7151d839e11f1b80004db7a937d4fd2ff624a30951e4e87fc0921f8012c655c566c99bf3810543ea56fcba77c5a4e7861674941111a013f28269e6a4049932ec429042ce97997689d72a2e7ab5db0d6ae32ba5f76360cc3282feac56e3fc247a808b0e3e90f87364f269a0b812e1186ec5230a4ce4c4ff34c10c0188d21817aee25eb8a0d4fbef6130c97043e1619c7962201878829fd44ad0b3242960e59842c4acc65bf493651cd578e35942cb5e41e2dff476919f95bff057bc56514809f6a1344d72b2915e5c177ef68a977a84b635a2b9fb267b827ec1de05f093f4e5d3803f562963135e928517b667b58e98d0d0c05fc9a7aee55ba78d8029d56b36dde17907da6331c046f31f7b3744cbf71fd2b50564121710ce057c9a75055f83a7e26c23bf712acef8c081194486154f79e756942604bc736f053fe71f92dd0d93f0304fbba45f3b5f57d07cb587ec781fd183bb6a93f81386015eb8ea422d34588569834e725f0dfe6429ed42b0a81555e7b4e90820c2def6a76b2ce3201bf3d4e14818f650552559a0d3a52912587f2083f5a77c31ebcc7fc5852256e1c68690d0e8ad890759c2d1b5c068cc64bc55de57ef4f0e73d71d9765c9c8a5362b208454db379bf5d2bcb4a3884df701a28cd462a645b863730b5d91e886fb354b6a1bf781ab0f8430251902567b66207c2c78b3b47c8cd490311c408f2467430f251865349088a7373d904221d75cb64a129f736d7b7e50569e44e60bf3fa856b92aa20601f99f47ce34961625fd5d0634ad765188b7396f04b14c0b584cb74c1c8e470bcf3a5218d26af328e04ddb51a720163a18723c7d5f1e9353e01d2e68f9e45e04d57dee23b5bd7760c86ffa0324e4945f360248610d40d33c56bfa826ea5d3c3d7678643aff19ee3a81e6344e0d48eb7c1d9cb229f2ddd5395e93b67e07ca175810ec95161aae1072b078b116e68fed63274649200a801e78d831a73f801d5b11bd530e4d85efaf48529c825cc35ff87b3a02386014e7385610f79b3be2b3cd14a392ff18430a1452e02c225b7cd1b803fb160b2a3bbc167180c600419431310d05261d309203f42d94a451741a338634f1d3f67b6e0ce1447c63854c43b0316ec2b6f537ab0c375fb1f964177293864ee2f3b5778218d22b3a26164454ee5e074f6cba38b6f13d19e2c74a17fc24766403072105df1d63450d35674acdde0274c5bf74703299053626bac40d54b05a29232cb0636ce5e242a6c984287717212d04cbfe2980c8825f0d36e83ab1fd435dff35dc004101f2446c60b47c255679154fa74a7162d33c6ee8aaeb7bae28050e864f6c030b9fa945bfd9f36dedf514110379754ec0dbb807cc4a6063c06023463da80809a2f96079db875256884b8d5b4a1e666b8da99a5f2a09be2c233444392f19f3706250561171234f17ed1cd556a0df9175730c96513165d115dfe4551eb401916c0f160b18be1e4523e671474c554a1c5158c58675e5b04e0905202072c8458c4ebac6210d2c1e23081efabc02d3925a5146c464416a7cae6e19780250938e9b3646776506e6495e79b0b6ae33d6c7e31119c39252c08e5e123bb59170c89855696d136515274528643bae230cd6499b07db8f5b3caccb15306e429057f90b1d07f3ba6670a8d44d541443e21016421461ae8b394c3734bc3afbd701011297d718284d4c6436e7a50d51a7d24db7cd795a39423c52b452e3387213a2042af09d70ddbbdd448fcc44326f03a160210ee35a096a843637595e2841a7126eb0ef2a4790b1bf3aa368b55385b88843a0c1412764641b180dcc8567362a4b41c2bcf44539fada64a88a590714b93745a987d970235a3d4abc64bf25fb22b85b596b4d29cfac3378f762307bb141a3600d2fbf355d1a24624fad396e7cfe67431a906b52a2e5c80c64af3a361d8c0b1dd1f4102c47afb25540596c3b44b4f75149940f0259e3fa71d3bfbd1e26d83a1de66ab71b6f7e8b63c2e796408385785d6a5c3a3e0023706489987915cf60cb492bb990575e967e4e658ca0432e8dac00b31bb146588bef19242eb96adc98ad25ce3dc07838c30b13b0191335866f1c2fb0763069804d61545c6d0650e78f3c7985f99c589ce9fd254848683dd2a4350a8ae9c65a5d1b681faf95607094e8a642312ead01e34dbc32e6cfa971d3f0984da7148d510682c149c218a70034d6ad745a84416e4f3fc71d3c0e847543538e7e36e5d85250e1ea43cdb08e0d19b2262c3080b32cd509455e978ac90e3bd3550bde443d207b82305ee2165446c0ce6a0b85c2fa233ac90410475ee56a3b82e042db109b70f4f4e910d0013f2cd07f3e5d94e2ad3edcd115701a2458395a05960010feb27ab409e7764a2bcd6e0fe3b23a80a4ef47f0159a057e614d37b172301a6ec80073a3ebb17716e1ba5ad3283c432e021d6181f1005c1fb388735b97e837f133f24eaf6b5f449ebb7e0f71ca217aa41ba77b1351a579d0944b575d07e27306fce4094dbe4e10fcb40612cd3f2c0b9e398c761677b02bd3065937a3f6836437507979a71da7026a03b64ed3de8711a203ab0a147c50074384bd44c2a9476bcdfc875a1a278225a5611e19698f82447c48ed20d3c4cd5410b885061640c8025e55d7055a64ec639426d81a3763e71a1ec0133948a78312be7fbd39503d4c0f715d8f710b979750f88b6f572fdd0a0ae10eb97542e5f11bfa3b533c46e2c80d2aa2503b433424450bdc0064e803ec4ef73e4c7daf46144f2f77885701ab3035da7ff77ebe70de4cdefaa54c3e37480e4cafb163d0771d23489af259ca5dad3116bfca05d670c93daafff030ad0b8c6abae60c13d459e73b12de3d3590f5fe4938aa701cb3db47198c3a0202b271d76dd58bb524b9f600534fc071713b50fa3584005d527d46302b17ac33571acf2e6ccde2b51950a2284fc049c24053e1715d21436e793aa0dc275a3ef93e018961059692593483bc254eebedc85e8f618a46c37dad04e3148b20806336148b1e2f030e58542179de843a49e4477bd90454404ab09640ca99a22f0d96961bdae9c0622c9176213170b148747ee86ee59bc116b17e0a4a62a32052b522ea076550834a031d651eebbcd04fe94fed6f2eba19550b84dc4101c10a4088a6ad058dea4b6378218b5bfb325875e727f45651782f5bbcdf7f179738e5032b558965e27e152f5de1d90cf2783d1aebe0cd162cdd112deb95ad6f23a9727c89d1bd693dfeec3ca4d340604497825b1121ba21d59c982404ea3763fd360b79cb39e107bda203186b3f55798ff2b01f7355906f703959152ab4384f89d55a3084f3d03a73ff2e20e0aa83251660d435cc60143f46f1b133bc92441bbd8ab901803922392c3bc81aaec749603335a807687af708c157ba36af3b5113828f0220e319275ba92c9a1281d7544ed0f8a17568f2f4071ae919333193571b2a19205638590d75eeb5e73c720e1d0e125e126534e2ca29dfc2243f77b1c4798e9112343b843e1f6fdb433a61ca493350b9396742719a74da8f9e0909d278798e355665eb11ed7a15f4a351d216524e74dd675886d498335c3ea548753f5f5e8f40e0600fce9b28b3891a1da5563138595c522e03519c17ae371040156da74511fa0c4b1d543127a860f743db6a3238752d9a770ee07041dc968b3123ec4570f8c6384a246378559af8441128afa44747eaa42faf21323fdb4cc364efdbee00ca2c7a55062bd00aac3b82643e4a4c39409f274ce042fe55ac079a0350d1ce551c1c572db3589511c3e14710e02c7068b415ac19ae8d6729a328b472653ee941d17981568254ad4498269712cafa440f7bc2c1629eec016da41ed6534234f774011b2e7b17c9db7a3fac6c77e6d5c95692844b092c5c4900bdccc362014d40751e305303ca701d23d8cebd2aaac927538107b20f18e2d740256348019999f579ea0c0b3ba9b21a38cadace611efaf01aecfc532e75b1f038ea195c3c6fe5c306591a507b7b97d05ed304184c958ed4385c8eb86fc2bc9a725c8e8225bc5808314eec7a60a715f879fec8834c7d3a6c2d9a9adc1697b6d74feccc4956d7397c508a8c284b3aa11e596bcd865d2b313e34813a9f3529f28d13331eb43cb4e7ff3c7ddbfc0996e9567b1b9d1036cc858e302ac99b7dc6628d104f98940824305254011bfd7c842fea78142bf439a743e003ecd4b932dcaac63ca6da70614867e45cbd845b3db80f1b5194ec6e0f28c3046ff271bf71c77cdd7d35713f3d0acb051c90c38c247548ba16286c2007a9a3b467188e57390fd0385fa963742b75972955764097642ba5ed6fb33b52731894356e85950974c582cd2b29db52158187743ce4b3fb0ae80b435f07b0f20994890674f6bb4579862ca01829c3ee70257d715ac1baff0942a42a3593380d3168899562b777fd2349ede9790f54ae1d7889366b808bc54e160bb53aa155073380cbd10505eb3f3a3d138e38f6a6af313285a2016a35e80d0f45f2456015aa1bd9b18b599d2c9e083309d817a166874be289c622fa2ae617ca89d6004d1468004964853d60f1e572487a51010cb28033c22fc17935e22c6929b2f56b372a2f7226e2902f2297ea1d332a2a1d76adf67793583d75a098c572dc1d214c2a86363554cc5c7e5f068b2855a9191b26e59b3bbee36a6633bc294bf769396a8799537bb99dfa7e7531394a829c9f5c0312e67cef83e83fe4bacb3565847e750998b839a804fe610f03b34b2ed9db57e194742c64315c57661c9d3f7a2fe10279325d77d09c8d4ae3534423ad53b565f89b5b77cfce5a67c4a26c57ad658a1fd5db0d184c42f336e5c5c35e6e8f0f72f5be9263fc51353c1a1d15254578d4616553e1640eb80453eabbc27095527d513d8090623184871a8eebe922d513547c12a8f81a605e206372cf4b117dfba87bf7039b418abb17451f3ce85c8f393109c8492e750fb61525a348354a571a120907e620207c819b4c7adcfd557202f226c7e22609989c0317080b6856f0e8163f3ea2fc0a38522031ea4bb30e33dede5e54461d28d0b2e8318217662749384674a9160f40426c4d59c6568440493595513e94d73fd4a02d5ea39ea144ef451c4bc8a39b69c38b390e1d90c322f2393e07efd2f1688056b132dfe2996cb6ba6408ec5aa23dfec53b534eb263595157ef53f5a5ec57c608353b46c9b34715625a5a699738110d7123408440830f65c449789f6d4e059eedce48017b095f05680d2fee2c3553a5cb1403e8f4960dd7623e77342885272e16c1474a532e1fb9054078bc6c3d5fdae3dd6bb061610cb830f25562977b4d4c8a45505fde357c0a09d01f02a3a35376a0d03dd8ef695669091d4f5ba84103737790367e00a372a18fbc7266d3d0636210eb0c361e9868e8d9866543815e670f89642d28152173eaa6fd4efc3f2d46b3358f51fb951121fb4a2f395f15d53f2e006a7c6ea5b9364365c5063daf687c2626b12c326927605a9443192e060f717d3ce60fa28cda5e04f2a83edf30f06ea8ac1a49f1f9e83cd21b77196829156e6635704d3bf76140b5f2e9266f66a978e71a8e570f3492431845bc28a4ad33710bdfe34b701c5723c1eb8d5eb9a00950d8ed8373f019ab276ce29f1dbdbe485fa8b258396879074418299c434a15a047ae4a692b4a44446caada473d43036307b072794b9bc865299bac741af52e9c7ceae440127554e90ab24c6f05804ef63d3b037a25a5f87a3a0c7556445d37cb7ddf74ad62a30c992e47e43a03cd88cc498d7cbb56571d112388c914281da96723b218051c4fb1f73b6f376369e08d48569e824f02f407736ba6a9f910fd84c5608fe0152cc363180ae4d19d701ef0f86c989abb50c37ce307843a193c066bd07b813a8a3552a9dc6ab000ea08db094f72dbaf1009b3fb9249c003ee217e20d326af1e692f91b3ee64dfab90070bac272c0634e57d8c1b6476e0f4e35a8e37377dde6d4e7c0692b6239328261e1106e065b486c846a98a714a3fcd64789d459823e0f07a3a21f9c94125438d5bb633406ce668035d7c6cdf49c0372a2592a8af5fedfdec299f97432f59d4ac38484a7f66cc43fa0014aa905b8e00583cc396ef4256239f3369546f3722fbf572a3874925fb5d7541ab1afa4a647ed43b27bb3e752a97071da19b7d578546077675ea20432932a219e4d6f40191d7bb47b33d3f6205b00e46cdc1203735b61c544baa3320693cb6137d64376fc3516066b2be08694784a75878e2202c4ac99223a52dd8658a00bb13c0ae4f7b6d791f2884bede3fff9dfc270a7887035542054822a73a5e345fa14fe817b41544132e62ee4aa6361344aa3e963d6268952b5a15fa50530243cc7c2990bd026ec238b26972694150804ca51ad91455669fdc0240fd144b545f2683101cf71734c273817b6cb4e541b9620a6c52f03d69bb87f20631fc7f207cb31212a03d2c703ae41845aaae62364d5fea1942ebc9562b61094d30f16778a96b62401d8daa343d11d331966b5c0017b674168a60b478c1e4c60121630e7bd16ecc24f92b04454c71d36250cf7074c1eaee467bb1f420c5194e4aaf248053ffc4b37b32e9953b7f8dca1432403e029ff55710f8f441551f5a471426416849a2369c30261aaa10eed12e14d6a80144d9cf517d3747de2da961536f026a1960ddd6c0076baff8478aed161584d67949e6ded26c995717080b848b32efd03618f6af87412a5b026a21ea6405f5ffaf598b90142035f24704af4fce29720f2a14e9139a6ef03a1446c55e9476cc75b40e30d0d9526736931a01c1645464a3526d4c794b39954027146068e22cb1940e3f3b77520ae8cd964014fe17537d620b6e591929246a4a8f1b50555366a11c9f004d73570cc51d206cf89645151212691b982b712c59e6c20f34f6f66114dd79587e93ec42b5d3980ee53be32561ba276bc03bf844a0cdbb3eb9baff31145b93144393ca318f33042074d95c5246dc8b41a0a25d3f36322246f630174d0cffb75fcd246f1dcdc9a65d8f95ba2d73644d06bb325535f2de12679464013ca3f4b707dae33e5da8d6730e0ab477437f1c37648334907846612d4349313d757ae47c5a207a0e47fae0a846283b462d7e02675987341f67097b88441ba55b491300ab4860aacf59bb70634bc6e88d18f02dd34f64162545643fb226ae0e4f33e9357818dba9a943f385a80baa569a4c8486a64b3ef19a446bccbd30b32d9c595883b430bd15da5808c37f3d774a1f4af6088e29460e3d50eee2bc0a28e62b656740907abb286120dd9f4b1c0bf7d8241d3d6613ad68647c32b93f2c6942a2352b750857d3fc7b03fe4505105b6ee626d3d42065f8626836f857aa00b0dfcf1fc34d9727b29f087d9ce1dd38986c440366439c35fb73a251d0012e6b35f9fb0e13d5e45e0a4ace224adeb8765c486a30b593230fa1f9816369243f0c72794e69c48f302be89f0645a9c3be5f6837e41dbd343a7268b232489264f549544abd78a62ebc3a9abac97663ee6618d12902317c2e304acfaba53302dc7b5aa65866173b37c75244eea0309b5bf168ccb8b43dfe49d3504225f64b3a47555795ffc75dc0bb6677611fbc71c9896a7e4244d705890a5b07d295ba521986364a1012a708eec8710cb5b939672a399f61e9ed266524c8e957d74be113824fd2325d49c13f78f5ce75ba95a5339734a44c2f53c1098fe931008177e169c0703d2a41fa8226c6cd8c44e3375f2b791bf417d53c005a392cca7ea5a7cd41a0d4fe0b6b42c62bf1dbb52c1731a5332027396a8209de0e9693605e616ce6647ccfa2749bdafd01ec782113ba5df95c52c6bf577d137b0ab93edb5c3e07bf1ca6d58a365adacf0c58ca7b0087dcda50cce2664de5c40e49e6648a15cfbcc23125d2af6d8dbb55073d329b69be28f25c55ccf6199beb286ddf82bd4e103e866728386854f3c5504771ae392d098d27322f62790e61eaa57918014148219f023d983bed47944f3d20a9b6b43b03f4d62c2de0e306e40a9b62dc7c3a2dc63b7736c7792e1639f16c2d7508723876cb7e4a0e3c3c54bd134a1b9bee534a7037cc5b55d96451c8ed533465a5b14ab01ff27b58080c6c96abe65607d8cc7e15bb23535da58727b737fc577b365804201f22065bd97d72caee42722a64a74f42e16442df70bf5bfe847866f4931e628575e6795abbbe5fb0a6450a172e1c68fe0ae53b9441ef380df50f573754422144798c5ee885db5a4ca4fa2ce2eb885592a7073e40ef766d1afd365c8232ab10dfc84806f5f8f046b0464c61856490481ebf980a990d8c20141bfb581ab9e8486b01bf5fecfb9830a411b363b486893c4b78f5452f83de36fc02e31a91c1ef401a6ce568f326216f582970583d23e35991c1ef401a6ce568f326216f58297058afdad6117dfc982a0eed0440e9aa2659b8f4b3330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c351f63d22127418fcc9457df52561187353670bb8bdd124ff4f251e2acec53cb4fe3b406712877366ede8408579404de43d8a015d21ef2f75f9cf06bb31b6180ffeb84f1ba46f4fff1e14683231b00ccda45523118cdb78488c8a38a3b3c10d1dac2e2ec351f63d22127418fcc9457df52561187353670bb8bdd124ff4f251e2acec53cb4fe3b406712877366ede8408579404de43d8a015d21ef2f75f9cf06bb31b6180ffeb84f1ba46f4fff1e14683231b00ccda45523118cdb78488c8a38a3b3c10d1dac2e2ec351f63d22127418fcc9457df52561187353670bb8bdd124ff4f251e2acec53cb4fe3b406712877366ede8408579404de43d8a015d21ef2f75f9cf06bb31b6180ffeb84f1ba46f4fff1e14683231b00ccda45523118cdb78488c8a38a3b3c10d1dac2e2ee97b6610dbbe6a43e555105b2590dc11f7b1cd2bce59fe0d602a5a54b5109a69f407473dc1fda3619cfe201eb23b92462240305499225d1535b0f0649669103fbde4db70f69447247f325f13ff99dc5284d5e678b934962f5325a53a6d80ab7dc668e1693e84af32065fac5044ad900704399463807539209d810b7113091a3eea770c27a64cac7d8cbd2c1fca9a25635f5da31d92343a4bfc537652e4167939bca38f7d10960f696dd30d3d39b9ee4642140a77baca3719f441fd6e52b1564385cd0c1d5ad89e74938f30011b15876d894a9f6b6a362268809f6b5861c98b329bc41e7e6d024a67a962e437eda30c5b831e3c68cfae135be5190749c216531280d26d4bb3e3597ab8dfbb260733e121e17ef8508fd3dd32c700267b150bb3719ca4674dbae09d6a35124510ea890a2f8ac6635355b349643cc2ad7096cc2e157fed49165b49eb45dfced2514bb4af397e65a6277243db25e9d9e2057c42e43c83b2f514be10ce612b549f4e51dab161d6335d03c016783de344c856094e612b31663e60d0160b792c8e466fd0f380626e65da1f10121f3adfad847268ce9c3aaa08c569dc262320b4b9607b6c51292366769f01ec555b605c0a0d1bc1688f76736bcc2172f8ad604f2877104ec1462d60d5431b22ccaf4efcd13c5475f773274201cf174218d833ddc698619cf5ff61270b666f49454e2cee4b230cda4dc45744aeee257fec215672db8c5700000000000000000000000000000000000000000000000000000000000000000000000064f331410010a15b81d0cd16c9ba1930e30702775d418a311d6d213f0000000000000000000000000000000000000000000000000000000000000000000000005f63235548e378772e3c4578df36c96c3f032707000000000000000000000000935b536b0da136469370392842e6f77c2033576a0000000000000000000000000b3c9d006e6e0940c69a3e73618c8f5d78c09767000000000000000000000000c7b94e208427b05a015acd78ae4ede20ff9f345f0000000000000000000000007280f67bfbc51a1fe585b4139e6d7b6b641716350000000000000000000000008f03be437c5cd03f8c75cb768fedb948abb03b320000000000000000000000007b2098414bd3c04ceec4d5132ec95f5b49bea775000000000000000000000000eb7cda75306757393ef387300d303924f141755a0000000000000000000000000337a01b58f5015310cd647d7ffc2b094eef342fceb1d50f9c221c2b3e7cc61d3778e13606bdf5709b28172e27b0160793c3f60d643aca24350893713f84c41d625df5776c6b254de17f820755d1c1560a2ad27b75606a26bfc45e720000000000000000000000000000000000000000000000000000000000000000000000008231fc1283beac40c71bce2c26a5ef16b67fdb6e98221130428198020000000000000000000000000000000000000000000000000000000000000000000000006e25db3a604f1b74b5e3174bef05770c959aef3063680c76024c205f0000000000000000000000000000000000000000000000000000000000000000000000006ca4e660af38e645bbf9094a85702b11dd3c79730000000000000000000000005b9e46320a31265081d71b0b1bf69761ea7fbb610000000000000000000000004fae873bcab5db1d71454e0413e1bd372c137d2e000000000000000000000000bed1521544c78554a9619a34825088157d46dd22000000000000000000000000d64a0747fea43757bd146565a2aa8b2e0db8166a00000000000000000000000039fdc4001eda2005ee3e1229d276ec294375e17d000000000000000000000000c4cde90acfe4eb440f3f9c59bea3761053ae756400000000000000000000000022285a37a9551050ceb7b93dfe20715585e1745b0000000000000000000000004a1de81050484018d50e0438c85af263174be936736f142037286e55b34cdd6c64541504ee539c17ecd8af2601fa385d2687614e86858a363d39e135c28ec67ce43b902825bb797459c36f30bcd4411687c9db5fc54db70f14d9307a00000000000000000000000000000000000000000000000000000000000000000000000020774e1ff61bab4bff9876476ab03473110acd472e1b0076a2eb835a0000000000000000000000000000000000000000000000000000000000000000000000002e12de53aae2c53e85507f3689ee3642be85d3711305a3317b0de20e000000000000000000000000000000000000000000000000000000000000000000000000270aa52e0bb7df7780584e23a1f14a6944bddf3e0000000000000000000000001084a94cf1e55f1dd2a4c048ec00c55a8fdfa73f00000000000000000000000064059e73fa7bcb6384ea77047ab60427554e0551000000000000000000000000ccfc0e56cbdbab582ede183d11be2a1d326dcf3a000000000000000000000000447656323e04016ddca5c85b6978844337e1a50e000000000000000000000000dbb1593d2d4d0528607a433115c0d146534c7b11000000000000000000000000f3bbc5403f56c4452f09e80836a2255734bd632300000000000000000000000077827973ae94757a7eb4496b2378323c8ec7c124000000000000000000000000e40e235ed8e2a73af55597503fcb6c37a2eb9a4a4252550fb5b401687c2bfe7092b2e1545f9da63cd051840daec78a669c39be50cb435a3ad711316a3c03250a14be901d092c8b422a415d1084f9a87dbfa9646ae3d0ae3fb56d1637000000000000000000000000000000000000000000000000000000000000000000000000180dc177b99e74226786d22c0b13046ee2fd6519fc138354db102650000000000000000000000000000000000000000000000000000000000000000000000000a7d02c6032ac29331bf6d03bdc34d248a862530eff430950cafe93330000000000000000000000000000000000000000000000000000000000000000000000001e12820ba0a8a95d162f6f2df276be1f077d0075000000000000000000000000f955c073b6747e3a05e88730568a644e2836df780000000000000000000000009471fe465a15c50be82dd20f1e85b16d8f7043220000000000000000000000009534b351b265975189359979759a193eed44b85e000000000000000000000000c5753c03b9fcbc50672b665d4292bf7b42265c69000000000000000000000000c7538219d7e6ed44bf967507b43fd9076bc5391e00000000000000000000000037fe4d5b6f270b59f6bae508a2da3f01975456570000000000000000000000002c7e702597540a48a40ab73bca8ccf742386996400000000000000000000000067a196526f57722cf38cfb0aa2a3af225451c459d7e83534461bf153b38abb0cbf90da04d242cc64e83e477a1e3f2c79c4822735e7afcb1e14736e02a3a49e4a22a57d6a8a71b10138df921f3a80641619e5380b90f305505687980a00000000000000000000000000000000000000000000000000000000000000000000000058904838516bd2740936007194fbb5209463137791841b3ced85a31100000000000000000000000000000000000000000000000000000000000000000000000064a6122d3a5bd02c76183d282cf5135a284d7314a883ed6e80c15674000000000000000000000000000000000000000000000000000000000000000000000000fd21f0012ef18337f068be0a93a1044ad5a743290000000000000000000000003b39010f8a35173c0f3d1d7bdd1f3b754aadfb740000000000000000000000001e6de91b906a0f3c00772b3ef3f1125b6841e163000000000000000000000000c0fb453391159e745f1b3f7c684ef75c12e8e3120000000000000000000000006ade2e0eebb7f405dc6d9049cf4217463513685900000000000000000000000060d4d478f7f1c9246874975c73f7ab7db9c790730000000000000000000000004fbeef6d83039e301da9580f98f805202e8e390d0000000000000000000000009d7222681ab6d4759c2943545659ba3c382c1b38000000000000000000000000119e9e61675ce43ca22545603c97b33d4b5d5743d9dbd55ad6746b675c86d5213beabf3e4a49642bf2d30051b58d6d7bd9a0de543cc7f73d9260923de02b7f2b6a9ac459470c136d088d0a2a9f47380f881549196b0fad1327d03f2a000000000000000000000000000000000000000000000000000000000000000000000000b77353650172f66f21ce671b6a77ee66c5fad3695ef7ba3c9bee8b7500000000000000000000000000000000000000000000000000000000000000000000000030bffe33d4f4c01501b20e15cfed4c0e1c88ec1b8510ef3c80f52e66000000000000000000000000000000000000000000000000000000000000000000000000ec56f901222fa7748eb5525ae144267963644d6900000000000000000000000000d21e4a1998d44eb45eb7440de53d4dd49ead610000000000000000000000001caf7426c1c9051aad8881539e1bc4798db61263000000000000000000000000d635092c49085c6322928f34b340a2131aae2f640000000000000000000000001d984555774cf41be7783e7e0c91a52267106e6500000000000000000000000018d1b003a5f8b31f0fadec390d190d05ddb4df7c00000000000000000000000056205c7b7728632a05b5af638d329f014456d1080000000000000000000000002f921a4b165c05091182f40265b26430136ede0f000000000000000000000000864289710619902d906c41644489ba046ff18160d1d506377e2b59012c01975ebdc3b82ceb3bee03cf393c1202ffa51d14003679db3bc037b166dd27b0ddd81cd2d5d5054188ad0581253837dac3fc72519ff34bf0c1917d1d3dec7e000000000000000000000000000000000000000000000000000000000000000000000000480cd2504257ef79e833f270d3ec551daf129942891f7c31c12e2b6b000000000000000000000000000000000000000000000000000000000000000000000000964f85269853eb6efb41640dc36d8756fbe64a439c460701776e7e300000000000000000000000000000000000000000000000000000000000000000000000005b4c7223a17ecb011f984854c585a36d882baf07000000000000000000000000327bd3194bf5da788da98c5935a3d95b3e98815000000000000000000000000097eada4f840e2e1835dfb44e1d9d6d3bc61b6f280000000000000000000000001623a81387a0f32b0111d01008b73f1c37a67956000000000000000000000000fdc186356a5589147ee47f68d2289b5aabc0d772000000000000000000000000ea9fb6388d460456c022d90332a7d11083e7693e00000000000000000000000089c0e82803c8ec0fca10226347c9a4181f049e3b000000000000000000000000be133b03c395c506ff94c3207944847c54f67d5e000000000000000000000000722e12606c0c103f5edf2b129afa911caeb6463ce8bacc316784fe1ec261a844d6200243bce136427a9923773fc4c045fa924863a884f8140396f555429d660017d8f566f4512f2e1b41451c78fd9f4c660e8221978b894172e56f0200000000000000000000000000000000000000000000000000000000000000000000000003331d01a5baba09ae4c3374237c172f902edc5cbb7d6d6dae146330000000000000000000000000000000000000000000000000000000000000000000000000d6ec0c33591a72294f5000759034080bb9e2a226d4c0737e30443f7100000000000000000000000000000000000000000000000000000000000000000000000027ffd604e76a1e1615a32a2732532e1fa6ff0e050000000000000000000000003bd71d1005e1cb365d5aa5780b27c848ca093a68000000000000000000000000e6662a74022efb5310255224783326262919343a00000000000000000000000019ab91025a5e7869377e4e120cd97c0c51f7d843000000000000000000000000bc444d6ea327bc65b65d602ffe9bdd6b62dee02b000000000000000000000000ef199417837d9b6c68cfb0642ce414314993a555000000000000000000000000a8e0f733b036280640d0bb0194ef0051ea1ef24a0000000000000000000000007ded3263a25df968d78b9763d86c5c69aeb1ba290000000000000000000000006c1471337ed85c2bb6c9520bcd419a4afa1e06763f3479356a36810fde5d875c1dc1904ea0ca6d7544e86536cea1085c6e87652dbef69e0e0dab7a6e3f96761184692e6473e5882a4a1be14ace48f451e70eb16495fdf53c67d28728000000000000000000000000000000000000000000000000000000000000000000000000f1dee434b3363c05969702262acadb5c92a58a12c627542336c318170000000000000000000000000000000000000000000000000000000000000000000000001228de64f93382303610695b183d4b39ba4886490438545074b8573e0000000000000000000000000000000000000000000000000000000000000000000000006fbfa23329b9e174016ace43844a3d64433d8f590000000000000000000000005a784d694129066bfaef513fbba0b154e3725a480000000000000000000000004999ef0eb5fc0213c22a237d8740731a39dcf86f00000000000000000000000077d8c66586273c73149ace3d10cbc1565f429d580000000000000000000000004a24583ceaff3378034dc80c5cb5961a062fa63e000000000000000000000000d296394993bc2c3562897c107182832d0ba20a20000000000000000000000000d22dcc5e874e085dc17ae8243d9cf55e9bd3fc55000000000000000000000000e971c95cc2a9d24adc10d438c72b8d17d37ac767000000000000000000000000d8d0d42ceffd63155f37fe0cf5ad3228837e8968e873a531c1e0c54b83d8c4097abd613399597b709e09b37370395d34068ca349f83ab158c267e965787eea45176073354abd973185eebe028aaced1c8f21967e05727b5ef29e5413000000000000000000000000000000000000000000000000000000000000000000000000529cc977c5e3791e912868638fe1583d6ab111595b17c82582a574170000000000000000000000000000000000000000000000000000000000000000000000003978c01d7e2bc969225fdf2602a01f1344101b693c984768ff694215000000000000000000000000000000000000000000000000000000000000000000000000a4098712561ce91bff59e40d7f6f651cee280c110000000000000000000000006a5f6d3d046e8533a5d6da72a972963c76ecea48000000000000000000000000ce28ad312a4bac522f28da0eebe06a7d209dde06000000000000000000000000a4298d7e3d95c232d3bd4154f3242b74311d79150000000000000000000000004379b3768233b870233e377852c1dc5059fc0667000000000000000000000000e1c2e81b5720137c097b9e4f00056520bf1fc730000000000000000000000000c870ad64dc37b87c8338781d166d0412848aaa4a000000000000000000000000deb1ab2626c99260baa4075b44e191483f99a84900000000000000000000000003c6bb6ff2ee546e6de2e95f84f21542dd3cd07528c6af330a554f39bdfad56eac931868ccf91737537d993d7bf62c286507173cb6300b525b05790741f15f63e7567374fddeba2bcff8411864cd2e4df4817e417fdb9e634452634b00000000000000000000000000000000000000000000000000000000000000000000000058a53f5c3875995b7f9fb8444ae63d0c0524cd5a78152253c3f02305000000000000000000000000000000000000000000000000000000000000000000000000fef05f0d19076d5c6b1e8e59d517fb528c48e9678493a16ca823716a0000000000000000000000000000000000000000000000000000000000000000000000004daa991a96baca3d5dfa956e7f26754f3a4d0b5d0000000000000000000000008d542d1e6aa20614cc7c0e375e19e158c9f1eb73000000000000000000000000b7d6c528726f8d046c0aa64151cd726a6a06cf4400000000000000000000000037cd26551adf6f1b46c5617b3141760570291e44000000000000000000000000c085dd4dbb948e1489a6a56850d71d4db93fb161000000000000000000000000cde6957c40e5bb6acfcf0907b87f4708471b13010000000000000000000000003669cc71add83d5cb7918d26eb01a650461b3f7a000000000000000000000000ad919b7434696c6fb254c02834e4cc16789dd44200000000000000000000000058c50e2c280dc544313cbb17c527c47a5bf98b1c2300454eed50a52d079b393ad6e5d948c9b51307b47df4341652404c923838373009f821aea8f020ed442a38d8385c5b357a9119ad85341bb1487f5564a5db50bb81e17e5b3a046f000000000000000000000000000000000000000000000000000000000000000000000000f53c9942e56d494cafe95f0464295e4a0347a537bf48e17b2e395d7200000000000000000000000000000000000000000000000000000000000000000000000047263e16d476e46c94caeb7df5de97477e97eb3b14f33f6ca5a7ed6c00000000000000000000000000000000000000000000000000000000000000000000000081f9784eca46915f1f08c176b8834163fc082511000000000000000000000000eae64c499bc5200938bd533aa927c066c2153d6b0000000000000000000000002296731249c90b5e18285a5ab61ab35efffeac290000000000000000000000006abdcc08acfd1b563aa0b91b8e887a6427d9a31e0000000000000000000000008b5bd667cd88e818eb4f4a39fc2b0734e3bbf45a000000000000000000000000b6468656635c0d326143664b8f5a392d59ff683c00000000000000000000000017b12442b63cc14d88119b6a5f6235029528d9780000000000000000000000004b6b841c0491a95990799a6277b0c53511bada060000000000000000000000000e75e57018ec5761b6d0051ff6d76d5b57ad4b764d88cd1867fd8f5f89fe2027b8940a5760b2ca05885a4b1d12396a424287a741e5c5a62adb74392a7b966f6ab9f0eb6d384eb07016a6de1537f4124bb7567707965bd60cfda8177a0000000000000000000000000000000000000000000000000000000000000000000000008cb74911368e120dca85b1799028f662c4865d441dd31133d6c47c03000000000000000000000000000000000000000000000000000000000000000000000000afa3c62fad7f6e52bd56fd44516d122f22a3e033a4cc0f38ecba054f0000000000000000000000000000000000000000000000000000000000000000000000005911d1701494083ae163976707721869116162340000000000000000000000005ef0721904160e4175b26a218bf0bb43e6a4c05e0000000000000000000000008d69c478a0a4e27d099b431672685620df67c07d0000000000000000000000007be2aa302431dd6ad624c4384bf83410b1614c0a000000000000000000000000b256882f1876fd318bfd6647e578af1de771fc5c0000000000000000000000002bab2d530c83be2864c3523245abda1085188f14000000000000000000000000af19b614638b7f635bab7a79071e0338817eb96b0000000000000000000000005a51f81b130a9c515e49352b380dfc7ac7b5e86d000000000000000000000000c1c0013f93d88925e6e5677e9a43ed6a88ad48570de20261e2351e6ead06f82f21a02e040643f10d23d83c391b13577e49dd81007c996f23838bba1a59e5924f1362c34d0740716ca4c2c4342a06362ade6a38212f71b513cdd9d26a000000000000000000000000000000000000000000000000000000000000000000000000f36a846e45bdaf0189dbe743831841265ce0693ec4b8bc59c607e54a0000000000000000000000000000000000000000000000000000000000000000000000008d07526ec018d90a5185b74029791757d2cf4823e03cc1794dc7b17d0000000000000000000000000000000000000000000000000000000000000000000000006ed5132324fb7d11503a757b1586f91ae20e1541000000000000000000000000378edf3df1bbc711e056290ca5811f104f2b28280000000000000000000000002c31422b75e6344897c82c2a5f1b2a60039e7d580000000000000000000000007ea1b6519f8da445960c5343269e4b5866ef41500000000000000000000000001e368d32afb16c1633c260512103e00d3fa89b7b00000000000000000000000099e4f5603af2fc6c2492561c1877642c592953650000000000000000000000008f5a0753d9d38745d3ab22414eac8e0bc795834d000000000000000000000000d30b4a740869c63103610d5336c0497e9c4b3736000000000000000000000000c782512e5ea5185e9bd2bf6e1b14ba4e1ae08c5ec3fb1d3112f0bd72dbb00300b5249a3d94f03f44a76d6f5177048a69aa838e2011e9294d18c9ea24b1532d184786f5730a91b745a5ba085e24f00c282c552b6e07ad8670ecd0611f0000000000000000000000000000000000000000000000000000000000000000000000006825d81035b07f368499245fad516d2eb5c49c4c3a0088764770d121000000000000000000000000000000000000000000000000000000000000000000000000564229605a146c6661c67741179e53689d88d2167a7dea593f0915150000000000000000000000000000000000000000000000000000000000000000000000003737fa346b0f3601d806ae376c0b3229e530c57e000000000000000000000000ed5bc860de46f4213270862711cdc97591d21532000000000000000000000000872ec976a40d901c4bb1cd54ee8f8173a31a9b6a0000000000000000000000004e2dae6405a6322726bd784acf52c62c9a7f224e0000000000000000000000003c9dcf3d6bedec59cf00666d195263758363404d00000000000000000000000081a1064655c860287d9757645da2ee620228020b0000000000000000000000009b97722b4b3ab170a2dc2e6c2d02e655e3498004000000000000000000000000d4bdee1d8eb54a776e1a532a2543223dd769e02a00000000000000000000000050a49c6b84866a3890283a40914451457282d40f51b85118b1702206caecda1f377e7155dfcb4e0079696a76c5c403168c8946460bba7b649f23d53c03eab46e3adf1c017c8a7e431e46ff48f402c875ada3891f6fba3d23e87ae1690000000000000000000000000000000000000000000000000000000000000000000000007f7e4d596f4f6d4b48a49f33b4ff277e6aa81310fe46e32bc5d6e62a0000000000000000000000000000000000000000000000000000000000000000000000001b80052daa3d765c8a7c51650252df2544a6b7588b4b32458f2fb661000000000000000000000000000000000000000000000000000000000000000000000000d3e7010971b8af28a9fce734ddc99f04a4289d240000000000000000000000008d234f282891b748d7e67e14813870242b90ff2f000000000000000000000000c9f0eb66d237d63d357aaf079938370044c9b316000000000000000000000000c9b6453f63ea827a848cc7032cc9e42189bcd50d000000000000000000000000ef92343fcd6f9c6fca455b62babb1a5215fe450b000000000000000000000000328f9450802fcf6b192c5647b89e9119d383902a00000000000000000000000035ac01444ddce1767d2efa4857f5cd54b6e0342a000000000000000000000000e1341620ff32e9325eaf6130c1142b1b3235c15d0000000000000000000000003b8f9b3e3a437677b40bad2cf69c5f27e1d58f655a2fb946cf9a1022750179600b2ec734ae9904660b071a4b9123f240bdd81f06e67acd164b5637278f81977032e37471d18be354b888935409b4421aa8628a021a29573fba32eb50000000000000000000000000000000000000000000000000000000000000000000000000a36323530be0416191b76c7c6bbde9388f13125ffbd0852d62874f0c000000000000000000000000000000000000000000000000000000000000000000000000bed1a040f862905ed26b313d051e7f2e43de52117ff52d47ef5064390000000000000000000000000000000000000000000000000000000000000000000000009d07d4372719b94d8aa87a0b2abdb557d36cda680000000000000000000000009f945f394190e449e512de6bdebb32239dec1c3c0000000000000000000000001b454528e54e9b6ab7e76d3248e0db128f21e55d00000000000000000000000087ef95107a0dd40e5511780c016ee400fc5b832b000000000000000000000000792d225b7750e73c9c794d00bbd52b49bbafc942000000000000000000000000fc0c12379fe7375d0f50954ac8c96d565a820904000000000000000000000000c3f49a0ef6ed6778055f2b379b21111c9675835f000000000000000000000000d662594264cf98450e73fd549407fa60eb9f2e38000000000000000000000000998a1e4e3f0d6c264127453307877f597259f429bdc3435d3edfc114734efd39b068615d3c6240406217b02b30bc8b0f4405263a4dc9eb3eb3084950456e5c4fbc7894129270ea5d5e50866e7861505db203dc6252c676193197311800000000000000000000000000000000000000000000000000000000000000000000000070c6845d8067d10fecaae25e17fa6657ce5e562f8ea70c35b8311f6700000000000000000000000000000000000000000000000000000000000000000000000002781d041e5e8b65d4cfc91baff39c144c1faf6a3840387ddc3e762a0000000000000000000000000000000000000000000000000000000000000000000000006d2a8e3cecc9f517ca835215c7395f70630466560000000000000000000000009c2bb970aa34d1290e31b168b8ca875e1c3b696200000000000000000000000012ef162eedcf4a5dc955e44921102f75816b3c500000000000000000000000001acb06646908a7013ae73d1ec1d3bd0bc8984b17000000000000000000000000193b741db1588d482479113abb9b976c0dd2403e0000000000000000000000001f333b0dd82d72299b56af7a80a79c3f09b11f350000000000000000000000002807750a765f323dd8291068fb3a2b1a70b5f509000000000000000000000000dd8dab6e045b5a2aece44c2d9fa1bc3331c90a0300000000000000000000000046d21b33fe7cc344f00c341b58185617bf80720bf5cbb56811596153cddfd3315a1e680e2229f95d63b65313be44c351d2999075c29dde6ddb5bed16afc19f7a7f3ffb6a46686844cc5f9a1538f33e28b6123564521c220f7b49f416000000000000000000000000000000000000000000000000000000000000000000000000bdf0ae3f6dd84c73c203d84796b8cd5f7081ea18903b1a33b1c9fe1d00000000000000000000000000000000000000000000000000000000000000000000000067c6a44bf1686c75715c0458c1204b6df9f1d70af4e74f3006b755090000000000000000000000000000000000000000000000000000000000000000000000008068a8206422dd455dc56e75e2763a4b2a83c9340000000000000000000000005cc0a26f3539e908a632c90c38d4c13271e3f654000000000000000000000000d772ca59bac99c4c5a1a705b2301fe340390e37a000000000000000000000000b665c26001b7c418fbed54639d93652241f7103d000000000000000000000000e035f5186cd91310cc632a316ded7a562429054b000000000000000000000000653526750a64260948340c1970a5f16bbe12535300000000000000000000000077b99815300c08129bf1be5fbe22384750c9b81d000000000000000000000000c4cb024cfec0753a5c6c5a0e43fe460064d85238000000000000000000000000ee2edb6134fe406e418f2d0e68134d107a2dc04533258c056a964e552077ae64b5cd68750a9026329d3ffd5c58e47735184a3f41e05d17672b33425bf1f7516c00edef43a0d59f397797b6402c76d26bb9df4a327ec2473abd7d70530000000000000000000000000000000000000000000000000000000000000000000000001dd3921d5743b80349771972b474c86742ce4b67b45979614c10b42d0000000000000000000000000000000000000000000000000000000000000000000000000db77810432c1b3bf27cd24de1189425296623321a1631524c5ca9620000000000000000000000000000000000000000000000000000000000000000000000005ad01327f376f9492398ff2fd3f57f329b2951080000000000000000000000006dd03b074e245a672c84527308143c62659d5319000000000000000000000000201d727d92496e22515df23ea726a7354438587d000000000000000000000000e9cebf21e7ff977b673c3f5a1376396c7903185700000000000000000000000058bc7f664a7ae921eb5f314a0e1b7239ff9dd73f0000000000000000000000003f234751eee5a03966442d45d1fb033071e2ec6a000000000000000000000000b46b4978bebdf13a200a22076d84ab776edb5a2a0000000000000000000000004915a97378dd65239fd11a68d21fa2255376046a0000000000000000000000005b2e684b264f5f49e7c744126a718d005c8f567d7b6ca43e6efeae00b62215611e953406db0aab3b5b5a8023c732243aae709668d4577e6e2d6df25164c92e02b516a71a09bc870d5e2e051789187d5059fde721f7b9fc27cb80ac520000000000000000000000000000000000000000000000000000000000000000000000002b2a445db4358139fc889356806b2f6e26974a788d3a3c48c1487a21000000000000000000000000000000000000000000000000000000000000000000000000f295cd26d2fb961feb70662f5206467ad00f696967e60c34907eda5d000000000000000000000000000000000000000000000000000000000000000000000000cf00d55c632d041a71b0f2133be8bb6f6bdacb62000000000000000000000000936411420a96b60739b81c56a02972075576ca560000000000000000000000004ed18a049b8f82719b0ea157c76cff2a85554e490000000000000000000000004681d85e1489a31e42aba37aeb5d9f0d821bd534000000000000000000000000c1faf3089b9f477a7624d45d664670369ea1e8780000000000000000000000009080c449a02f14353227f65999f2ab72bd30f85a000000000000000000000000e60ef514ea91cd06003ad35436610d4f5bec9605000000000000000000000000b9de791d36bf7e12a321705c57037830ff5c4a52000000000000000000000000f36c625faf24404e42146c2cb93bb64fc9f1db42a9ef142f5f3f6e1c9265d72effa998706b207d227ff0243cfd37e863b0476c4c841b503231a0800883180a0d36a96b2a17aa293c279cbf4d07cc813d2557ab3bbf6d3b152f2daa31000000000000000000000000000000000000000000000000000000000000000000000000de51da535b34ad2862e8cb28295d8767e0c2733142e201102e509d1d000000000000000000000000000000000000000000000000000000000000000000000000b4d7ff2c80f859184f92e15abb18c41d265ffd3d60e089091eefef0f000000000000000000000000000000000000000000000000000000000000000000000000ceca8e0a7316103c353cf87189eb9069ebc3606100000000000000000000000032179574bcdf370620c5b75142e1077beeec79070000000000000000000000009245d466da9c110b0767e4676833840f23d7550000000000000000000000000067dce16d367d285566780e6afb9386224b38674300000000000000000000000044fd3910126caa32bb18dd46dfdd326d37a71d5a0000000000000000000000000ea65417f550f260e1aa24193bfc05647d9c1a00000000000000000000000000e3164e0f0f1821022e23d526f958724167537d6d00000000000000000000000092ef1d29531b3e47b4779877bb37ee1a392b1f6c000000000000000000000000bae4961fb01092603894611eea43040683fe2471ed217c31a50e800148ced123bb33a04509c07d76a82c0872d5f483734393fa635db18e2b35e358537924b66dbf13a729319e8349d9adec14501a0b42948d2f4f8664944225ef315c000000000000000000000000000000000000000000000000000000000000000000000000e50b91628d7441509a85504c225eb525d2a889694db73d38cc44173f0000000000000000000000000000000000000000000000000000000000000000000000000ce2657d4048c215a056e549e3149d4d4ed8946177461004d29c41190000000000000000000000000000000000000000000000000000000000000000000000000ef74a5fa7b7fe3d0eb6c5728ad7411b1c08215100000000000000000000000080f766181f69cd7bafaa0206e4d1980bf4388746000000000000000000000000b535f27543d2c847a604fa3c0c6d7e35e44e2a620000000000000000000000009744a27489a53e69f15c3634331f7c6ce685593f0000000000000000000000008943e2065a62e23bd91d3764b5d967486773591a0000000000000000000000008eff0e015534f92927e9701d87c2a331b6141339000000000000000000000000010d8d01c6505e346c119e7b427e0c61b97b9563000000000000000000000000c529bd77179775491785ca47e682e67b0891e7070000000000000000000000003bf5ee1c87c30355caec5b1e8bcec25ffe3687797fd29f57ded1595158a43e17231b874912225f7450abeb6e1c432349a8fd5f41f545765995e41a56e99245263683643529dde03ada67c87daf4750366286e13ef643dc61abdc2b460000000000000000000000000000000000000000000000000000000000000000000000002ba7e0453f1f2f2f2a1765783a11061f62548b0d1c805a5588679b1d00000000000000000000000000000000000000000000000000000000000000000000000027d2e559d2f92d28122eb419859fae7be134cf208dd1f07e3eed0276000000000000000000000000000000000000000000000000000000000000000000000000db228b276fe7bd359cd25a1df3e4b0125c269064000000000000000000000000c2698f314521161367c83d0803ff6b4264ee2c50000000000000000000000000f667a643bcf46f31c6a22156dbc3387b0eaf1434000000000000000000000000b6d0d15198fb773b5ce9a473e78f2372b1e39f14000000000000000000000000dc8d937d237199508e11f267c51f3a49ca27f544000000000000000000000000a2486c1349e69b1158a28f3590c46107d144056700000000000000000000000058e4745b766735568a16d4770c77e94afbb42b3b000000000000000000000000bccbcf4da6602f27b5ead15e0f69303e628ce82b0000000000000000000000001870ae030253a51639a208328955900cb7f2cd4e0ed2ec3c02d3351b1e35e562695ca45b8448b47104eecf03c731dd4bad7b2e6b0c5cf67af0e2196959495c2c935d093fcefba43593d0bf78838e6a5a1b75cf565010fa4165b28f09000000000000000000000000000000000000000000000000000000000000000000000000f385fe145ad776044d0026128ad94d3dde25b26c71ea863db0bec7290000000000000000000000000000000000000000000000000000000000000000000000004e68e16a5ba3296b04f0bd56aa63280df0db0d558ea8cb304969b37b000000000000000000000000000000000000000000000000000000000000000000000000ab21d7329a1e8e5f19cd0d362f475f0855fbe824000000000000000000000000420957214613e24690ca6e659ec4cf22dfcdc8260000000000000000000000003fe0e5243533303dd90c867949e29b4a1419ec4600000000000000000000000012bc5c688e5c125ae4bf2e233a57c231200d94150000000000000000000000004a3a75084a7fec2aabde4a54fa45d90a1b6acc240000000000000000000000004ca1d00968cf2128d554d150ab6b9c29423e302500000000000000000000000056f6491bd855ae1c3f142b312bba29387aeafd4e000000000000000000000000c31d521e1067f7092e560d70cd41eb09455199040000000000000000000000009e5bbb0c19af8f51ae550963baed012db10fea308eb8152257c35666d6b3694a362331085aa6c4341e438e17d4deee62856aac68e8a61c38e51ab54b5680d75935cdd63c6f6f6e2da55ec1303ef433577177a51d3c27cd703647d70e000000000000000000000000000000000000000000000000000000000000000000000000b482cf3f08936f14a126d44ea9a0777da16588031dd315000cf18220000000000000000000000000000000000000000000000000000000000000000000000000bce48c4a48772c65825a1e45d615857135009a419addc82b53263938000000000000000000000000000000000000000000000000000000000000000000000000b4c4b602b0c99f164d91730ef990951e5a509c2c000000000000000000000000fc0bdd1dd821e94d8f6ec14d32a1dc39c13b5d06000000000000000000000000c200a41225bb9f4ed7e89f3ca1b4747cbeb6f74a0000000000000000000000002f79690dd71e1e097f904f729a76b7121e964846000000000000000000000000956b8e19d315f470d82f3e083eb02d1171a80c10000000000000000000000000e195830532942751086d813cb0d02d7e730fe02c00000000000000000000000091f0846ee5bbc550d57c3f76fb18065c2758a052000000000000000000000000e4fad6795e2b6259f32aed06d158cd36f16115440000000000000000000000000d7d5c4b902c1c55e2949264d65fe9527b28596797afad54f99b874edac40e1cbff2531e8df57a4333dc853e8bd21615aec7c941afe31c5536aa3b482e26947009a88a713b307c523b97666e572297682c46c56a3f45263de7de44290000000000000000000000000000000000000000000000000000000000000000000000003f2d21260fe8415a6c03233938da3871625079001aed591980ec7d6100000000000000000000000000000000000000000000000000000000000000000000000067dad24c3a38b22c3cae9002d9674f64413ddf38d005b42a067ded00000000000000000000000000000000000000000000000000000000000000000000000000e9884f58ee230b176e8f4844f3e6827c0b85ef2b00000000000000000000000054513f650fd08a339d8b9774c2074e2c97b5796c00000000000000000000000031944e1859c8370dd45a6d4df4744b6fd7c1722f00000000000000000000000050d8b32f6d34b47e53e0613043a471609030cf7c000000000000000000000000e830622ff273b414c34cb225fdc6ea7a4122a3280000000000000000000000007daf1c1a1910f57eb03515788766b666bfb98c3c00000000000000000000000044e3705242a731708d9ae765ec534a41a90e7914000000000000000000000000735f961663678c6ef9f3c62efb3a0e5f118da52e000000000000000000000000e7707b3d3e3c1f4bd53a331107b4fb65fc983b6b10a864044ea301028f45a729252b0012de7c7d1f89b6142f36e5bf1f7568c854a697cc3797c57f099147911047ec0d545058b6097d59ee399d205c083696271e9d3dd75a71e5eb15000000000000000000000000000000000000000000000000000000000000000000000000b1b566340bfcc41e7563ea56f843eb54bab90f29eff1a94e9d5e4a43000000000000000000000000000000000000000000000000000000000000000000000000c80b2c4b465a183692b23a726ea6a019f278f059a55c497a66a89d110000000000000000000000000000000000000000000000000000000000000000000000001223730380344f2858cee504b34ca92942a9105a000000000000000000000000cde70a7ade1fb557d1163e1a54cbaa3a9d67df75000000000000000000000000a3c4ac58ca05ac1a90d4010709184151fab5fd0200000000000000000000000022222d7cd7c95a6b9471617667f21942744c42710000000000000000000000004991e761105c3765c85a9934fecb0a4e2420d07d00000000000000000000000008b7ec7951db2b74c5d892456c3a9a2bb2f8525f000000000000000000000000b197cd6d797b6b6462426d36a784b85c3420e471000000000000000000000000ee457800a3aa8f2caa0ada6f4b3fdf56e8c42c4d000000000000000000000000d48c3a4157c4ed298502e1289cbd5d76a140c11f9c988d6e5dd698579132e0317a88960378f5d1064315c66e297def47322d007b0e48f463e873f30cb210a75f7531496fe99c6351ac55635ff661575d5e1910358e6d9c79d758da3f000000000000000000000000000000000000000000000000000000000000000000000000030fc057137d2a0d27a19c660382427edafe06293cabb303daa1d012000000000000000000000000000000000000000000000000000000000000000000000000d7a7221b1cdcb628aac2ea7956b2e41cc73ff5691248806ac64f7073000000000000000000000000000000000000000000000000000000000000000000000000759d3310b56f11786389da353110f71b8f5c2d1e0000000000000000000000004ed0fb7d35213c4cc588173fbfd0ee7ceb1e1a540000000000000000000000007989982e316e6d2205452361c7f36e4dda9f2920000000000000000000000000d189835e66fc646b95bcac328655514fb409316200000000000000000000000073324466c838c2029693af53887a9845de243803000000000000000000000000902a862dc6417336c1814b334f438305a0fd21750000000000000000000000000f59a800cd74c536c35587158b95cc0b089e534e0000000000000000000000006c994e0f6ab36c1b91edf5044f4d8d684ba45b7a0000000000000000000000007c4cde3ba148da53fb7f5a60a5672e15e930d504a88c372a88c32d34a5a08a61926554482325276361c1d71f223ba459eea4f63dfb810251732f2d5985eb24278a3a304464e3ce462969423df7ca0d434c81c47633b7d2477d611133000000000000000000000000000000000000000000000000000000000000000000000000e11f9118d988a02fef305d410b54e920e6cdc62ec0152e579288324d000000000000000000000000000000000000000000000000000000000000000000000000d3b5724a793b0d71f9f37c4e4fe0343d9c6e98687b534c29f4ccc258000000000000000000000000000000000000000000000000000000000000000000000000701c9a572080b71e463e68377ea3be408f249559000000000000000000000000fdc7af21fcf3aa2c3ced4075598b121c0e057334000000000000000000000000985a3b64007238573c29ee3bcaf8224e92fa4f18000000000000000000000000138ccb5c3b14ce6f7685942692207a0ed687f91e00000000000000000000000087f3221fdb1a8d2532b24e067efb040891732977000000000000000000000000b4a2af48ca0bb25f1b8d844be3fd341aa08101040000000000000000000000005b35dc5b55289445a221fa2fa3ba54180d671a250000000000000000000000009d2e5041b272734ac429e86c47723413d57bbc40000000000000000000000000d1f318091efb0b13ee71422e8165720f008fae123b3a4106c6eb8a3dd56fb9623ec4fb0d24818508251f2532880ea832c79dc2625e5b60594550c12f61a00007bc27a26e1d2ec73a6a23030f28e96b37a8a0c55afc454b4e9a937950000000000000000000000000000000000000000000000000000000000000000000000000c8d1921a5a2b90277d977b537f02ee4d84b0111e036f1c61fe721311000000000000000000000000000000000000000000000000000000000000000000000000f6ff1b7d6737a7058673512925af6351f9625766bb77d831509f9e4b0000000000000000000000000000000000000000000000000000000000000000000000006443ff5e8046f424a3db76185f07ec5fba0d23380000000000000000000000000a5b7b0eb700eb06f6a2472e3190c537df98cc2c000000000000000000000000d528e33971ddc832b3410a2c3d006311b270145d000000000000000000000000b2144e5ea33f4822287f9607daf6fc40b888f420000000000000000000000000fa50e80714e47f7d51e7c53ff0a1a4049bcf3a7900000000000000000000000043bf512d5f7b685ebc0f226b8ee90543769aa3550000000000000000000000002da1d8666ee86a1b9707614c6d11e058794f21570000000000000000000000001cc5ab4752eaff7c1130756296da46083d9aa05e000000000000000000000000a61a29495e90785c39ca2367e4975d001a9cc16512bbb77af574f447acba6d7928deb36e965cc06818b7a137f17b3047f22b5f2e989a6211e8972b48b73b0e64756f4c4cf9fb3d56cee083139bd84d281644326477c4a83427853f2a0000000000000000000000000000000000000000000000000000000000000000000000007d9fe94e8d16a42b36a0722f558dfc4c3b101d4c57fe34215abe41520000000000000000000000000000000000000000000000000000000000000000000000001f5bb41b34ba813f618dad70bbb39177514ca50d3e97281b1c41553d000000000000000000000000000000000000000000000000000000000000000000000000e9939a411231b07c186637550742720251b08d0b000000000000000000000000ef339008ef80482c250a5a388225d52a7a6b3942000000000000000000000000d53a631328a36c7d7105025f1d0fdb18898ba45a000000000000000000000000996b5c36a96d67577b468076d13f292caea9d07300000000000000000000000001e77632f8f9d750f029c63e4877dc128bcdb927000000000000000000000000d065cf5bced1336f25e03d0cc8b4165dc52c7b03000000000000000000000000cf6d3013dc674545dfadef07af894a2c2f524e6e00000000000000000000000080e4b33ba6236d54ca7fc137fc04020a750acc2a0000000000000000000000006bcdea2c4c93ea33f126de44ad3b7c06be217f07278465334619722e8b8ff56616261f06649f3b134369ca5783cb794b51b7ca470b56b579588bcb37646d6a51482af35131be3c695cf59b06ecea5217069bac0fced97979ea4b000b0000000000000000000000000000000000000000000000000000000000000000000000006031c21a106ded317e4092295f431f0b6b6f2b3f86d393600d3744700000000000000000000000000000000000000000000000000000000000000000000000008c166409397b2b37b3d7001916719904ce76d271853cf0725ac52d6d000000000000000000000000000000000000000000000000000000000000000000000000d716ca7eb543c45bee5011085ae41a36a0f17615000000000000000000000000ac46903b96a2d92027338171cd9c801186fae971000000000000000000000000f16aed7475273d2f67bc09739f6b3b5d8e1a822f0000000000000000000000003271077a98b6a07e5739a354f2b6b03cfc520469000000000000000000000000f6e8b0396d30227e0afade634fd2b6283ef0a75500000000000000000000000058814c056383bb6430aef44e4bd29d52d94a06550000000000000000000000002294b868429b396bfccedc4d5c85196fdba7377b0000000000000000000000000215fb483a1db27aefd3f87e69625563d2f2ff50000000000000000000000000d4742a66da3ce754dd0b8310fe780f4d23ede71cd8ad735ad513935fa5927244eb8e7454f0c3fc3197ba635a617b166bdc2122715d662d28bc39d9197a629d766561aa2b3104641f9a24dd4c6333d83814a8f1036b67750687314338000000000000000000000000000000000000000000000000000000000000000000000000b008f74d21049633f138b259137aca5809592a75d3177840b386bf7500000000000000000000000000000000000000000000000000000000000000000000000096d65b4f5caa5f23380f6f7465fa665daadeeb00d6f04333a2d3a510000000000000000000000000000000000000000000000000000000000000000000000000080889138a80b41b167c7a673a7f4564fb045e5c000000000000000000000000a26a084eda746e142802690441d8a86e7bebfd4c000000000000000000000000b8b02779b408af670586bb3c5f92594067bcf80100000000000000000000000025fbfd1e7c04f7222a42c2078854866476b34e5100000000000000000000000034ba2906487647203a713206cd7c92210025c472000000000000000000000000836bf909b2a7bf1cdaef784feaa8755c7777a2030000000000000000000000005ea2b77d499d37403f3ee5224ebcba4e8ac76102000000000000000000000000191a5b28be18ae75e6113a72bc291859a5cf9515000000000000000000000000fefbb12b8eab871fcd83a2600897f43bafc5ae0484c91331e99f5b2c2657a83e425610701e3e10746f039b321185b93238b7e05b558fc50f73d43c511ede433ad5f3fe758206b23b198f8d2fc2984876fa039c5ca3ef110196201f4200000000000000000000000000000000000000000000000000000000000000000000000011be2b0cf702b176f80f226168f601739d441d776d60667c9ba8da0c000000000000000000000000000000000000000000000000000000000000000000000000924b9701038565718510b91cdca9195a3999bc6be4ca5c09cc65cc52000000000000000000000000000000000000000000000000000000000000000000000000a06173223d40994b4436cb795f2f5d3c8423601a00000000000000000000000074fb9778df5af060ef9f152a52ebfd1cb8ac9a56000000000000000000000000830c0d3821701a47968e01601054347cfb63a91e000000000000000000000000db0a0d6275533a7814e77c1d64823c3672aacd6c0000000000000000000000009a090d66d3e75a44728eca22b6efe8293b9d6e77000000000000000000000000010cb46b98bf1e793dd4445406cd5379ecbd166a00000000000000000000000036b7786cbe3eed4104b0d304ac9eed724022c73200000000000000000000000041e8e366c2bba602bc1ba2671e4eb82028f9311b000000000000000000000000e9790e26b7f0aa5106a60404986c6254b581a658e5954d01bca31c62bcd7901eebbe1f402c2a596b2b32422f44acc42d5abe245b373a9a27807ee066cb99654f0f03a411b93d1b6fc12c5062070714218a045f1df593d3443b89bb280000000000000000000000000000000000000000000000000000000000000000000000007f41f17d58c6d22cd757715cc8edcf00f076282b2c41aa017ec3930c000000000000000000000000000000000000000000000000000000000000000000000000cc9bd43e13b39d0478c1c61beba73821f50113329b229641e72846250000000000000000000000000000000000000000000000000000000000000000000000003c3d902635a94d677202ab18f0c7850bedc9093e0000000000000000000000001b012c70f3b7665facc7120dbe17e36ed38d1311000000000000000000000000315b7e73fc79666d6776323836c027484afbff6f0000000000000000000000003a7bed1d121e9008b46f845a222f2a7c32d15c730000000000000000000000000f4c457745dede345827bc3a823e8b3ba403142d000000000000000000000000054e736a45fddf3c9404e4133bc31c6ed8b3b4350000000000000000000000005da28a1283c14d44019cd95af14af1517b00fd060000000000000000000000003d6eb31d494f1f55c16e1a2fc8e0bc22b9205f0f00000000000000000000000064d1460b242edd1118da2f7b659a461d3628bf74e774353524cb7d5ffaf8755f7201f2629d0ffb03c1fb6b1e0c45385d7c74b055c542600037f925600a1c4b3521f275091e75655335e5ff4d76a02c3d4e3b787d07fe666de51a3f560000000000000000000000000000000000000000000000000000000000000000000000009a139476c4891328931ec82cfc9b4128f39ed13ef3f4f82ce14d0e0a000000000000000000000000000000000000000000000000000000000000000000000000cf1d816d139c4977f3a55c202254b625e1c4526d918c190f9b590531000000000000000000000000000000000000000000000000000000000000000000000000bec4f21bfc082918ec9bad4118aecc29f30aaf1400000000000000000000000085cecc73a0122e2fece5e52d7d6e1a1682f2a206000000000000000000000000f9f44f340e346a215a9ee640c2f04e72f0a4e02f0000000000000000000000001b92df53841bb46832650d2e0cd88c0a33212f5e00000000000000000000000002e16a5d2de1e64cb27234348579a303298d40780000000000000000000000006cad361823314f7e9c558678a248f054f218ce58000000000000000000000000b14aad37d8791c174a39442a45244503e6e0c037000000000000000000000000843d105718c0464307b27b2dbcfb4f4370e1355d000000000000000000000000cd1b375b24a7341651907607070d3c61441691142031830c78724b5c0dc3b9345a8f880e2e2df44552fc276d6ca2582a492f6262a05b523c63b0c565281cb43d1054fb298b2e2b712f18675ea796fd5241e8554c0b9ba66a845eca45000000000000000000000000000000000000000000000000000000000000000000000000d95ec76fed42466074b43944d4363120bab21a5bb1a87f06d892734f00000000000000000000000000000000000000000000000000000000000000000000000079349f782380a442f446683ec84f3b50a40430796fe3ec59c3d76c4a0000000000000000000000000000000000000000000000000000000000000000000000004944683f3f4e5d2a89061803c72a7868beaadc74000000000000000000000000928c751728eac048bd1f336af9f3b0375cb09d4d000000000000000000000000fc79dc5d4e860f679e1f42735b60fb09023e8042000000000000000000000000ae4761166083913cdb7a900697d9f57bba920f5e0000000000000000000000006d4aa578f5e5551bc65dad68f4170a6962163e6d000000000000000000000000de9412657177311e160c010a1f4fdf03586bb511000000000000000000000000e3e62d36c46a1466d92c3b039f116d7d8a9fed47000000000000000000000000855faa18b9828f2ce44e700426ef46597ffda00500000000000000000000000003d2637633b7f0512318f912ce74a53e5a61e90330ad3c3ef80f053ef10517647750652453190069cab22c49c90fbb74ce32090bf9188f6c15669d55312ee663a9919e6a8352cb550b16fa4ebd5a7f2e1bf62c71c0ddef7deb0c044b0000000000000000000000000000000000000000000000000000000000000000000000006196210f46ac9f1b1b2f096a20bee53ec603b01710bc725906c2ab6a000000000000000000000000000000000000000000000000000000000000000000000000992aff0997ae9e32690cba7c4ab4f177e2407a0c2182fb479601af6b000000000000000000000000000000000000000000000000000000000000000000000000e47f11023a7ce30589e5483d4270eb0a8addb00b000000000000000000000000f4c0a07586950d246185bc2404c87a20252b623f0000000000000000000000004643f0029b192f75b5c88624c116e9400431b57d000000000000000000000000db7e6c06cf45b354f0021f4e40b1fa5e1c576661000000000000000000000000499dca1614237569137914762bd5b5698aba490d0000000000000000000000002d765c46cc1c9d3052d64e3f01ee4624a691d6470000000000000000000000005b2b6608047fbc7a541cc60a68cf692bdcc290280000000000000000000000008d964c0ac698e612ebaf0471403905122c460c160000000000000000000000001268573792ae3b7e613dd351932d3909c31780117f094d7caa2662039f47891dbad53a1729b3a9656dc7cc03a8561d253c9cf926830bd93ed24bf32f7e8dcd38491ede4673d37d5b79cec569ddb739175bc8cc2e56ebd1052474a02d000000000000000000000000000000000000000000000000000000000000000000000000af01ea04e63ff846380d724f87b24970a8447b6fa0dcc60e57de9270000000000000000000000000000000000000000000000000000000000000000000000000aa13a22698298a08662f1f3f648ccf42be4c0d44e9732c7a3b3db419000000000000000000000000000000000000000000000000000000000000000000000000d03fbc1adf4f7749c3ba9249bd9dd9765894fb48000000000000000000000000a516ac2c9766cd493c6c9324c6ec6c26addac027000000000000000000000000faf9d638d2d77c60d5d7f2407d94f01787656705000000000000000000000000a961372f8141f16421e92a560c5da40890b50b5c0000000000000000000000005b464c64887b7f72ff38c71d16a32a2141f30526000000000000000000000000aafb9c0d6a0b8f4caf9f68442905b470ddc866240000000000000000000000002599d45a454c8802af07465a78b17d13b4740761000000000000000000000000607f5e416df814306c6a195b108b3a28eda5df7100000000000000000000000016844a4012c25d1b3b49254579fbac771f9f8e3d434e424e53ff12695e0dc76b0337706c8db25f4d73d8a11c6ffd771c5219d931fb0b596de46e0b182a99866cba91b814d5d97e1cb0db011b43e1880464a52a2a3cc5b86273b01751000000000000000000000000000000000000000000000000000000000000000000000000e8323962a79aa253967bed20bb6cfd2c7c67121686a560248686983a0000000000000000000000000000000000000000000000000000000000000000000000008815ec4e1d0f4b51adc6ef02f455cd1d7188c96a208d7c51392b40250000000000000000000000000000000000000000000000000000000000000000000000006fef1073c7d5be06dc364115238ecd401a30f023000000000000000000000000840c5f50841cb535e9d73a2719da53424007b82400000000000000000000000059fa7632fe30956e83f0fc72690da87e77754b190000000000000000000000003b41e93afeed2d19a516651ae5d8c55adfb1097e00000000000000000000000087a99c262184a6210a09c45cbc77e80f586ebf2c0000000000000000000000002589f530191950539ec3a515ba73bd1c2f4dd245000000000000000000000000f3ce976ecc39de5d7164be69885caa58bc6b175b000000000000000000000000567c360a2b023d308b8b902e65b5c10861190f31000000000000000000000000d661094ce8950f3ea2a0f11582a4a571fc76463748d95c57561c5d20c1d96c1e46ab61382cd71c2355d978698401344b58ad1b45c8fbbe032b11d74144882c31f280482909d21742329e4453a899b25f32c7477adaeb1b0d0776236f00000000000000000000000000000000000000000000000000000000000000000000000099b72f4e7950446c78ba7667fa015c7ac06b2d6d10bf9f4982fe754b000000000000000000000000000000000000000000000000000000000000000000000000c0137107ce2ad05d2133ef1b5407dd42cbd8200d1498495289583227000000000000000000000000000000000000000000000000000000000000000000000000770859178d74c43726056f0277636a4ad96d675600000000000000000000000083e2872ac27d56248f9bea25dea076244d1f6a170000000000000000000000001053c64c77d9c71d7645ca511bb1c765c97b9a2c0000000000000000000000003295b1382844ec6efd4e3b6908ceb83196fd290f0000000000000000000000008af0e0413bd0d00e08b8bb5df452366e244c303600000000000000000000000065f14058eca6eb7922b933406716ba34316a332700000000000000000000000081c4802009adac018ac0207519d7b5150119af710000000000000000000000001a61b2323aba905a228719554fe8f06c51b9b278000000000000000000000000927d3d478b2fca6333afe845a00db454a8311b200399734fcdd43e53994f1d539c51d630aad4a6649e24cf43508eca291213510bc628aa6d53752347c7c25f7e0fefd20987dc5369e89b6c5c479cec7484209879d2d6c50ae1f1d258000000000000000000000000000000000000000000000000000000000000000000000000b9f81e5126fb5e13b81f7523421ad653c1784d2d9f0ab95660e1417d000000000000000000000000000000000000000000000000000000000000000000000000c4604e2bae4f8543cd0d3947aa034f5f454cf4496f2efa0c6448cb710000000000000000000000000000000000000000000000000000000000000000000000000dad31001449ef42e25a861edf8e9b54f1bdb078000000000000000000000000c42bc555c6a215687a0f202784c34a6e75ee2e140000000000000000000000007624c84de276342af88d573f7aee474a3f42f66f000000000000000000000000c46f3a03262f37162c932b2256a3b90ee6b9026e0000000000000000000000007f99ca66ff1647694d4dfa14921e21049653a620000000000000000000000000c75374388c957614858fa41f76d3313d66818c0a0000000000000000000000004cf7ec3c5caa053ef1b11d511f692e49f424993d0000000000000000000000002616dd64e183ca296cc22405437db2636e95b90c000000000000000000000000eed26977ffa1a178f62dc9774eeab8030761fa0144ffa2299f000c71a8829d74998a617efde0d446a9ddbd448ce51c003371e5072237741e1bd329662bdfd3643b2e6d4cd58b34082b39945385f98b609054e948baef7226abeed028000000000000000000000000000000000000000000000000000000000000000000000000f341956a1a2a3f33da16625f165c71676dc1950a824ba5679a17f84e00000000000000000000000000000000000000000000000000000000000000000000000080f6a47c40784a5b6bee601ac5285071bd78e95630a2d6449929530e000000000000000000000000000000000000000000000000000000000000000000000000e714541740b7ba1314b66d3ca5f79f35b23aa3090000000000000000000000008ecc8247b25b6b7a39cab06fbf20fc653b736774000000000000000000000000a298ed14c4af08133485291bb83ed336a6342e79000000000000000000000000e4a7b2079ea4914b82b83e74372822587b413f4a000000000000000000000000e13f240a42650c502c633b7001e54159906f9246000000000000000000000000d106270d65343823fb14c81a1b448679aedf030800000000000000000000000099acd052e7713269db738f7dbbc33858a35ae51e000000000000000000000000864dc508c6f7d5561c7b610171eb0629206a1665000000000000000000000000d5026d51b905485e9a78dd40edc00c7a3f959807eda4d10068e85b2f8f70b56b76d00a563791a07e7462eb6f253c79320d989d6e14d8aa6f06f902054087d724296d6e43a3705b6a808ef670257c447a794bdf4a59eeb6680b9cef1a000000000000000000000000000000000000000000000000000000000000000000000000119fef367458c93dc2952e4006552064c59ce94eecfda562e9bf8a7600000000000000000000000000000000000000000000000000000000000000000000000021ab2363ddbb221ae604d733560f25420ff2ef71e3430513d058a901000000000000000000000000000000000000000000000000000000000000000000000000bedf90569a64b2205dff9f725b432f0afe54c770000000000000000000000000b25306785333000d34f2f20aa0b3503071e6285c000000000000000000000000a031fa7d8cbb4604becc401f5ecf912fda83200a00000000000000000000000004961d559d6728327c4e8a5bbedf2302b5fe59290000000000000000000000007dc7f829dd04b228b99b5c734c7d2940ef9ef01800000000000000000000000055c85303d34d8b0d7e8a966da612026116afce69000000000000000000000000b55ebc07c37d046c78f7ca1f3175c87423555a1d00000000000000000000000072e0bc5313ef9638f70c4a490c1dc52067035d15000000000000000000000000bd2a212fbb35070cbb73672fb7654b306f2eaf34c19aef62d515e96259e7876fd15b5d13d066184cfcf0ea36b56cc109000b7e62f1274c2aa5adcf4aaec819388304460d7d9dbe2c0554c97eb42d290070a0951afd22d4267c6d5c1a000000000000000000000000000000000000000000000000000000000000000000000000ecc6155f6ce96b33bf9369081811c74a14402a4b89c28429a054b8230000000000000000000000000000000000000000000000000000000000000000000000001ef5db35e0e35042943d7b51a1fd352c568888179aed5d378b9dfe6700000000000000000000000000000000000000000000000000000000000000000000000076a54f57ae712b35f04b252d14a6e079d65f00490000000000000000000000006864fc7da629b76380fba04da80dce2abebc6f05000000000000000000000000d358fd7e6d19fb3cc3e05b5243d1ad6e2e461f4400000000000000000000000011f28f12a4e8126241c9911b839fbb4a3d03856d000000000000000000000000b935436c144a1c528da50936f2c50f5a802a8f740000000000000000000000006c527d0ebcfc895104aafd4ed46e615835114e63000000000000000000000000ae809e4172fdbf3b8ca3617e3e882b4db08830660000000000000000000000003f43970c86cd20394d5e03439ecd05329cc679100000000000000000000000009b0b2e49c7e01534d87e8f04e7d31f0f7f8a58488cacee472d5488288ba2b102418c4e53a378da3582e84355547d622c4f4d06156e3a795dbf964c70e7a2f21efec6c05c528f3d42e7b5a15c88f9d369b4dfe97ead6c7f67c9c9f239000000000000000000000000000000000000000000000000000000000000000000000000e9346b5146287a5b94cb376c6327f236cbfea60b494d1a07044b3f7c000000000000000000000000000000000000000000000000000000000000000000000000fc79334bf31f244782a86004a4d20135490ffb28e074b80d22a594750000000000000000000000000000000000000000000000000000000000000000000000000aa2bc0409103b0814c41e1ae3ac353a67c4760e000000000000000000000000f90b8d7ebba238630d21462463c99a3826bd972600000000000000000000000086313752267f1e482fb56e4c849d134376652b040000000000000000000000009d06393b3c6c245630550b1bceb7db6380691042000000000000000000000000a8b97d236254db7a0880c21e4078fb211c86e512000000000000000000000000148ba742e0b70101cf2a9c73a40bfc561d81ad76000000000000000000000000d05e6c13537d7405650c6c043bf1c2788f999c6800000000000000000000000032f3bd3c3b337f5c6f030e4cada0e30e736d144b0000000000000000000000009d362e271e5e9021fd501a51477b2a42d80f9036fdc1862ff0b029154950ce3d948b5718ca124a70f04ba948e591133c22b31d7191a7982101e2db5be770ab389aac805a56409021ad98c148aa74502e5abc124159c6822a82f4c77600000000000000000000000000000000000000000000000000000000000000000000000050a0cf358384865930d3145b25a2543a4efeb570c1eda9768246430200000000000000000000000000000000000000000000000000000000000000000000000010c81f38ba49586461c0b83a097fa56732469a4513f1146b65b204680000000000000000000000000000000000000000000000000000000000000000000000008967464363f14b31d26cc842bc66ec1e76d25d0d000000000000000000000000719ab66bdea00f6fec9e0858c4c7ee1da3001b2200000000000000000000000099fefb09a576c1184d0129479db3112d0167a3650000000000000000000000000293f220ee9ccb5f64ce527cfc91df1fe444af7d00000000000000000000000016ab673039a7f100c48412634dcbe608e7a4c70600000000000000000000000091a4a40b3a5aa94dc8021b196b0ab83c2250142400000000000000000000000025715a1a94fb814ac9d15b18e1ff501349ac4477000000000000000000000000ebae0a0e6517091f902303666e34b613343d2d7a000000000000000000000000f1f4a10cb4b4e16746d9125fd291ad1d009dfd1573eb8d347f8b4555079fa9257c30bb0bde7ded7003c2a624fe40ec5d981a035668726a2a4bc180025fc1692b9a9fe12e7f258555af5f0827cd71fa7201f6ca2fe78f372c5f2d5c490000000000000000000000000000000000000000000000000000000000000000000000003aae853ef69f46564c7a6f3a4bd6e85805d866525ebe144385f32650000000000000000000000000000000000000000000000000000000000000000000000000237a3e20ecf1f96ab0a620352926763eeb953729171c9349e78f1e3c00000000000000000000000000000000000000000000000000000000000000000000000080b0d632ee6447738fcc342dd8a7e83498616f48000000000000000000000000149f8d78238c993407acc635442fed317afdf96f000000000000000000000000219fbe063cfbbf62b5b15b2aa76129098c989c270000000000000000000000007903253ffb179639affb65794bf670780c92f008000000000000000000000000e1b57134f4f0de0a98308060fa5cc73b2eb87439000000000000000000000000a4fb0e761152f6451bb3657175c5f2577abcc8380000000000000000000000001e3b9656b570a72718e6a71b74bbd3774d67851b000000000000000000000000b8e6bb6d97913f6fe861f235f513e2235da82a6900000000000000000000000046b2ad51b0b4126eceba6e3e7339d862729eba4a5fa857454d3da3064fe02226ab460048e739636cc198ca3e386bdb3f2aed40625936931e663b32016b981925e248e7691d90c04490bb8d5b4634d45d6398550347e570610effe3680000000000000000000000000000000000000000000000000000000000000000000000006ff522314e67620040c28a148865b86a441c115bac7b6c5488215d0f00000000000000000000000000000000000000000000000000000000000000000000000036948154de78db34d942b30fa5ded51d02b64155b8b6222b4573ab230000000000000000000000000000000000000000000000000000000000000000000000002949314ab7d9a54a9438920b7bac222bedc6617b0000000000000000000000000ee94c31f8e1de43ccb2d731c418ac4460281419000000000000000000000000fb8d687aa53ad1190cf2f011fd3c7d4e57af780b0000000000000000000000005fb7a21df61b537a25062553ab807d30c758176d0000000000000000000000002f1a9713587ae5681bbd8a6cc6075c0912669401000000000000000000000000ef10f525214a786065cb2e5b3d631b4a7b1ff067000000000000000000000000b89018155cb05e083302b707666cda58d57a2a3e000000000000000000000000f1250941a5ab9763edd5da1db90541232f40014d000000000000000000000000cf4fcc02cde60a624492655cfdd4b53d77460a4837e2d0206e89fc694b6015173278a6608a243f2f7d6fd271fa3cde7790d34843f3b74e34be53e75aa583432d89d22f795ebf483412d406494906cd48f5f8636132578f1c657fda0a000000000000000000000000000000000000000000000000000000000000000000000000484b5a4f10b26573e3a9e37e0ea7b66dea757c1679d11b63ab34cc6d000000000000000000000000000000000000000000000000000000000000000000000000484fae59f428410f7866be1f96640100ef633467e839a612f095ef46000000000000000000000000000000000000000000000000000000000000000000000000964ce32660b94b7bd088f73420cec8134b24661c000000000000000000000000b2359e03ddb0817b06b9357433d10d177a8ca91400000000000000000000000031e93b6b250fc63174c76b13eb12ea32df08ab5b0000000000000000000000008b1c4748a3cbab5a172c8474adb27444d59905730000000000000000000000009ef7322c56d3e508804dc617ea90286659c9424f00000000000000000000000044191f7e8c41cd3f389f92626781f41e310f040c0000000000000000000000007e1b360350c6ce5644ac6e1012c6de6aee96c705000000000000000000000000af452f0a9c418c4446d9fb0ea961501588260079000000000000000000000000a923725b0b9eb67df405217e23fc3b40dfdb6431631315390cafd40e146ae052b2c5a9380b36514d60432c7d2102ee472911ac48609b6d4292fb2833af495d0ccb1cb368259a655c06607d36e7a7996478c8192ac6da5841d3064b5c00000000000000000000000000000000000000000000000000000000000000000000000017c1b11a63bb21386a215e47430c43139d774d261b6251392b05601e00000000000000000000000000000000000000000000000000000000000000000000000062e7bc4f0a80a84e0f694543b88472148ac4a1309f2bdf2b508f4856000000000000000000000000000000000000000000000000000000000000000000000000667730513c4f6b2c313f9116d3794e1f839b431500000000000000000000000063859c1b5065744b3333c61d2e901655ad09536500000000000000000000000069d5d36ae337d20f2fc6c31c3b2e6703f9ab8873000000000000000000000000436f964bb2454d76503742442e75cc7d815517100000000000000000000000009601b41054f243216fdeb40ac8977c692038455100000000000000000000000013f3ea52742ccc4b0a0f9f68afdc7848632b4608000000000000000000000000bd3fbf5ddbd970502dc4aa19215ba864e598301d00000000000000000000000028242013f127ef15cfdbfd305104d64d9b4e494e0000000000000000000000009d25785544bc8e632740452035916b0607c9c83d31cc733d8323aa5a99b14929a731e62376c97e5877c12a48ba621b3142300d78e9390a07dc23f30bfc0b3b354ff5442b7c036e2bcb16d35ad54eb8135ef1545aeb9dab275fb08713000000000000000000000000000000000000000000000000000000000000000000000000a0ea323baf5b794ceabb6226b7ed874ed20f9b12d32cac4bf202f16c0000000000000000000000000000000000000000000000000000000000000000000000008fbd227e3e607d7a57c5c64059a5e767e468b9252cb59a132a1d5c660000000000000000000000000000000000000000000000000000000000000000000000000ff91e0cc19b2202d2b0ff196bc4e7638a4f783300000000000000000000000073a11d4ab0d5ad2e5d3c8e4556d2e01734f4a63f000000000000000000000000e55159055f2d794b4c3d91188b490f40e0eb4c22000000000000000000000000f672e66803ee204eb930a173bf314a5cad56175a000000000000000000000000a99fd57e24ca032780799f21ea9baf7e70ee594a000000000000000000000000885f9c5232833a1fa6a0b8070127fa64b8aab86b000000000000000000000000ee9e456686b0a4059126365334f7274735147217000000000000000000000000e0355b376e5cfe4f6a3f004855c45502c0d46316000000000000000000000000e604084e1aaee74a5253596a753c156393671b403e7ee10e7430c1644920892d9951080662e0aa2564d62710770173273a70510627c1ed7732851a7ade397e5cf9db46077947db04a44b890f5e7d607428b7727c51b93b0b109ba66f000000000000000000000000000000000000000000000000000000000000000000000000a1f2db3ecb5fd819a02a9f0d28f23b016a248e580431d970cb4912240000000000000000000000000000000000000000000000000000000000000000000000005ed1bb4eed377d64d3ca717ee8dad373bfd0001cdabf355317bdb607000000000000000000000000000000000000000000000000000000000000000000000000cb733877cdc6435c2a6f9e7902094c7371556708000000000000000000000000d78ad54f1a72ab04fe0e4421a8b1a90d48ca8d650000000000000000000000000c1fae03ea4f040da233411d83081206a592e9010000000000000000000000005169322f484f9957e1bac74fecaa201eba2a592b000000000000000000000000520d1775ace26f41b1c44a0c51e8c4606994dd1f00000000000000000000000037d5c55d7ddbc611892f4626842ecf18a3a76a3b0000000000000000000000007c78214a48c9ef701d7c407ebb78ee2c560450210000000000000000000000003adbcc4de3acb103892df8387aa2e97b093ff47c000000000000000000000000abf75c534f7b9e671699845099bf71535f3dd65ea67b824230587e2db5c5fb6d0b1afd09bf0c8a15ceea55128734ba4a1e1ec55b81c3672940435b19fc831a7695c98c42c98bb167472dfc361d7c7f3f08de295a218c3b5973f9ac4100000000000000000000000000000000000000000000000000000000000000000000000047d3244990037c31ec46ed027b596025025fc32ded7a6068a04e582d0000000000000000000000000000000000000000000000000000000000000000000000006eaf82400a8f1a1ee8e6ea272960a817121ffb5f7190b84f095cd059000000000000000000000000000000000000000000000000000000000000000000000000e7d652416ce7b177944f97775d34b460db08ad0c000000000000000000000000d659957d928dbb0a387f2f30411d760a072610440000000000000000000000005669bc364d6d2b0ce5902f2b0afc497e1f1ddc3a000000000000000000000000702fba10f6741c4a87854e6fb603730784e9ab1f000000000000000000000000c6c7de6db57fed482f90dc77d912db05a8152d300000000000000000000000004977077e814df32153a05a15de3b921818143657000000000000000000000000b852b720a4c593792ba40d599116b4441cb1e1270000000000000000000000005f79b805a0e84721293c6a6d7bfea800c1159055000000000000000000000000c7a2cb7e9a9ebe5f326d3218c1afce435865fd66a233cc511cd1495c9447481b08fec0685e4ccf697139d826349395482bc7e41c8605f905a5bb650bc27cc348020b156b6d066837ad4c6f13a470e25ba6cf721f54aaa567ef6f0a34000000000000000000000000000000000000000000000000000000000000000000000000dd56c2401b3aa63c799575515cb2ff6b85580e6fa578f26e8d9aae64000000000000000000000000000000000000000000000000000000000000000000000000f7e5aa239fdcbc6a3e1a691d551c5a2fe5364a2b308291424b2d3c46000000000000000000000000000000000000000000000000000000000000000000000000e85ca566b7937271dd466377e16ce940c29ff724000000000000000000000000c95d9a71a20e2004a2b9be1740ceb7604e73ad76000000000000000000000000724b1e7c4b1cb20d2043d041c23e7b3a04926d3d0000000000000000000000002377b5226fd2f62b05654b5466874a0a7a5cba1700000000000000000000000002e01671f1aeb11b4470c9659ae804497ce9bb1c0000000000000000000000004e6dda67faada1385c727a510cfabb432bd3f805000000000000000000000000c3a7351cfb439772df59ee70a6857934870f7d530000000000000000000000008a7c086003accd084b6e2b388a896041e7c1bc3e000000000000000000000000cd5d014a9154b2224008d25f70a36964be5e2d24a8fdd335e1b0fb0fee1bdd68f8b4b0188c76867746a0c61da4e89209b5d1ec6b22fd7f3609ffd30c223cdd19d69edb503f7aa61f94e3b3099773933a5b93cf38521055161623643e000000000000000000000000000000000000000000000000000000000000000000000000c348954d0dbce350d6832e7a2009790db9ac7f388e63b3141e17ff5700000000000000000000000000000000000000000000000000000000000000000000000009af7b1ca5b5ad62579f8c092ba55e0533156633986cd56bc552de09000000000000000000000000000000000000000000000000000000000000000000000000e8d78e02b38d9f37bc4c360d82182d7a31304e620000000000000000000000005af9921e0dc4477b2dae3b062c1cb87d164f9b0a00000000000000000000000062518b1c1301ac7c778937288ddec508e397831e000000000000000000000000b375c95a14cba52d67ed775e8f6c22799c63db0800000000000000000000000004a93b5c277e7459df2507730e43b461dcf16d41000000000000000000000000d01c6747060cc250c2b66a2d66217d3513a3a26f000000000000000000000000a1e32a2ed623fa019419034b46174d6d0a7e4637000000000000000000000000961dd44da7b8884f383299283bbe0613a58da57d00000000000000000000000038eeca7c7893ca35ee32d2251c41253d3af9cb3e59b84a2cd29da2204c75dd4ee146995e5226fb28c10431760b24221d2ca2e375e917830d189ade58319d3901c6718b3ea5bdad568654fd3a0b993f466ff4c12da305ea708d359e3500000000000000000000000000000000000000000000000000000000000000000000000017c27f644da08e14de236015cb11a6367ed4964dd905e51f0f11c707000000000000000000000000000000000000000000000000000000000000000000000000a1fa3d2186b74452b37ea3426bb9e6666f4f647089f3c924eb0ef7150000000000000000000000000000000000000000000000000000000000000000000000009a6d7e3595158e017c11563eb89045533553674c000000000000000000000000be367b05e7161a531fc6216a70cfe90c72aec3500000000000000000000000000dc6f61e402b0f016f76e70f16e0523e5e2b765b00000000000000000000000033be4b14f12eed1e777b4079b7650c6f419e9229000000000000000000000000fec3dc05014ec01a7fe16150a46b5540c7adfe76000000000000000000000000520ff669ed39730ba906bb7192e09e795992cb2a000000000000000000000000e159755f7b265114054ca9675703ce001bd1750f0000000000000000000000001fa4f86f031abc2b6442165502c7a526cfe6f3060000000000000000000000000acc75665c2f9659a3c5e56175ffdc7eced2790fac491c59cef1f736aa41512ca0cf927203f9270ad3d4b41c779fdb3c0e942717550abb059974347ef0e3de5bbcec972610984455a29ac947c1fa6e6dd6d1c26e61acb7184dd0db43000000000000000000000000000000000000000000000000000000000000000000000000eb12361f49539d4742dfa56c60649837ef377b25563c5e10fd541d0400000000000000000000000000000000000000000000000000000000000000000000000034893c65379eaf1106bdba2a5e5e8d0558086a63a9f8d62ef7ea831e0000000000000000000000000000000000000000000000000000000000000000000000005330696c2a240c301cebd567bc6654216df4ea3a000000000000000000000000a493f543675b583a3c15a07b13c01c13bf8f74310000000000000000000000009c97a8457169460842bd2059e419b3676421b51600000000000000000000000048edd9044bf1c268177ecf7b851911452cc0560b00000000000000000000000032f86a4a7e5e5d6fb6c4670c82c7a753d6bc47580000000000000000000000003abb6f7616084a3ea0276246a6ee4e624afb6f1e00000000000000000000000045855f4ce121dd76305f7709b0ca1216d7642c16000000000000000000000000300e507e9aa1022b5eb26d3635477b31aefb267d000000000000000000000000c985a1237f2d9a5d3928c2024cda412cf0c3f35c0e64720621352a68b6b7147557268704cd1fb36deba5fc5bd583550827934629e2e5de7abecdb847f435560840a6f3380cc7136c87a73c1c90f1066913c6fd52f9035a6428a79562000000000000000000000000000000000000000000000000000000000000000000000000adfa1e49a3402c1e061dbf6aa8078a7346ddc529c7a2bc2ec5e6811f000000000000000000000000000000000000000000000000000000000000000000000000e9cef25a87d83125b806867ca33e4d086fef425ed0d5fc0fe6db8327000000000000000000000000000000000000000000000000000000000000000000000000e210242a16cb1b59e5c5ef45ab22de187e9482440000000000000000000000009c9c9d7b296c74730d50cf6aab0aa62312705f3c000000000000000000000000f9b5881e0c3cf74aae4cc43ed3a5d03a3b2db077000000000000000000000000d13ce71693183912979d2a7b5fe53e2552a2ca1a000000000000000000000000aa8be44becc94805ee405257e9341625a96bb1480000000000000000000000009551d12aa08e8d068158040cbccdbf7627a3b407000000000000000000000000a5eb640ec3b3386ef0c6465fe7896318dbc889580000000000000000000000002fec5e16ced6d16d66783d2cabc0004fa8b85a74000000000000000000000000f8462428944dd654aa6b282002dd00287e796b3a399b510846a3d84167783a09e6f43d4d293ba12683cdd066d376b4795665924834f69b1d6def261a2f218e2f4e4a271c01498e27fe4566525fa9895f5b751c62b6c51143c7287413000000000000000000000000000000000000000000000000000000000000000000000000ccb702349daff34fb2e6415a22a2dd0a97f617350affdd6407aab26c000000000000000000000000000000000000000000000000000000000000000000000000b1135019e1ec550e17345050eed18a67b75c774169612927553c425200000000000000000000000000000000000000000000000000000000000000000000000004b9ab547cfd4352547e4f2a954cfd1beb941f0e0000000000000000000000004cb0292199f05c456e6247069c075573134f0605000000000000000000000000bb65342f535405616c19ee0b142cf66ecea9d9610000000000000000000000007db86f2234c33319b04a616493d4d97eb888b43f000000000000000000000000b9e35344b67c245deaea4c739c09b02cdb957e210000000000000000000000007206d6422aac70203050c13e259ea964304dfd080000000000000000000000005599605f132db7108c7b834d3b69421c48758326000000000000000000000000e21f2e73f231d24bfdd03a3aed469c33668c1801000000000000000000000000c3354163fa7e03147c06184e57448c536c57df71e3a2fc778729a576744cbf1db4088a20fe22e1039eb6d76f67684745547e01412f46e5288ea4e27570b5ff57c7200d04a2dc266ef011fa07450c8e09a3fdaf153e25a5772734fc6f000000000000000000000000000000000000000000000000000000000000000000000000323a842716c6c95ebb8d4f0136460021cb172e3345b74350189c7c7a00000000000000000000000000000000000000000000000000000000000000000000000073f2310a75d7da71a99a587c0361c85a015c895e91bc4228143575600000000000000000000000000000000000000000000000000000000000000000000000001c8f9b3f398e846edc4c01205498c1416de7053b000000000000000000000000950f1f22516fbf494d8dff746a5862725a85fc23000000000000000000000000d10bba11fab30202081dd93e7e822f3b9ed9ee000000000000000000000000009f0edc7ed0af093163d112565ce51c7c2785a345000000000000000000000000c2abc3462e67d9534979a732fd69316344ad4a100000000000000000000000008ac8ac639251b641f0095824dfeb060ebb92f87100000000000000000000000073a37e43ac0ee03720ed0a63c5f571088195780100000000000000000000000047fc5f76aebe2b10a8fd0b51ce80224660b4330d00000000000000000000000019555616bff55908ac96085489a3b81ee008ce6026efc34aa36494552e39671352ab08214f386077a4148d19d40441270cb8314ca9f5dc7d00000000b1df2d10b82b206478d89c731db6c12ad5a29b3602f8af1fc8df8e02ce42bd24000000000000000000000000000000000000000000000000000000000000000000000000d02aef34df49ee33f5774320c816245c2fbb4825be2a30498296e87c0000000000000000000000000000000000000000000000000000000000000000000000005905ed14bbf5313e788c8a2b9e71253ac0821b0e54b218576a33bc7100000000000000000000000000000000000000000000000000000000000000000000000046fd415c72010a15d5923e3b411d1d2fba1f0d52000000000000000000000000ae14632f9bc1da01ca76e35345426d624cd8697a00000000000000000000000016f1df598a133d009192f434a8191644ef288a480000000000000000000000009dcc2e00c98eee243dc5bd517fc7925edfcb00470000000000000000000000006c4b3865f813df0a13ae8c14fd938c777c12d4110000000000000000000000004abf203b086b3a6e494b2278c6aae80e7b0ec57d0000000000000000000000006a6b191d6a181b2f2daad91e1a36d424b5fb3e2b000000000000000000000000e00d351d65750977d2486b2f66c1f442ac7a443b000000000000000000000000736b7a1bd8629e5442d0bf7ee975025d85a155449658bf6dcae73547f76e9604d2469d6d43616972ffd8e77c2f3db8508d867d7b62a02237fb08060d6926b4713519aa7ebe82ea46aaa0f44f8088521b78bc8735345af95b4823aa4f0000000000000000000000000000000000000000000000000000000000000000000000006086eb2ea8e2ea74d5b9187806204345021eb86437796a0d841256240000000000000000000000000000000000000000000000000000000000000000000000001521730a30963f243a87ea23d75814034628931411e103276021fc5b000000000000000000000000000000000000000000000000000000000000000000000000a84ea57a18670120f8029f2822831a5ad378366e00000000000000000000000062d1fb7c8d06195dcf3fda3fbdee9c044276e30d000000000000000000000000d90b466828a0fe20e489fe7be1d7687bbde4bd50000000000000000000000000928d84058d60526a5381f24f97424054a8912f7e000000000000000000000000caec0a683275c5223b79a03f27b8d1629778cd010000000000000000000000001023a24922611a059b140e5b5f231a04c3c9a7140000000000000000000000003f89054802351b67d32a1d30ed9cbd3f5b2f29790000000000000000000000007279237ce501ce3c67d6b27b386dfa053f23925d000000000000000000000000c04dc72f65243c07d64c2f4c809ef039a7e4bf6e1faa5e189681a7163bb09b0a458fd97e55ef62349d238f06783f66774d2e6b0e060db9410cd136463504e272502def4f91cdc05e6688433aa673cc7d4d70eb361a30381653032065000000000000000000000000000000000000000000000000000000000000000000000000cf439e2ec0d5b958582787214a657a5a5504da676bc3cd0ae87f8973000000000000000000000000000000000000000000000000000000000000000000000000c73b170d9565003e9831024f39648f77565785388317fa7303f8d96a00000000000000000000000000000000000000000000000000000000000000000000000034dcb42ebcede6732a80f575f76b9610e82ad17e00000000000000000000000017c8e719b470bc305d2c8c1c4fa6522dc1719365000000000000000000000000d4339a5bd06a726fd449a160915aad0458a1b4550000000000000000000000004e7aaf23b568366d041d601cced7af3970013a0a0000000000000000000000004575a05fa4802b0eabaed22962c35c050ef03b6e000000000000000000000000685c1e13182fbe60428a63064806c3236662491d000000000000000000000000d8f13530d372a703560404715c74781ad27fb35f00000000000000000000000028cfcc6a93a08a5f61de902842ae4f5eb95477100000000000000000000000007896e706afb14d68df6a8d576d9c8943cbc5c9231b2a3773ce14353c992f88462cce2d662fd32231462f1565db04394a20f19b79349df83cf75b0e5a6629c63b9ec1b17ab393ae73d6bf51364070af25955f9b32ef668d7cdeb015160000000000000000000000000000000000000000000000000000000000000000000000006a8f334865173226e0140a6cbabdad3cf8970a64bd786a2967dd8827000000000000000000000000000000000000000000000000000000000000000000000000847053659d4577146fb863672201d57c436dd06c881bb37ccd174a14000000000000000000000000000000000000000000000000000000000000000000000000cf61fc112a1d2c4e664c431093bdb06af8cfee4000000000000000000000000048a16b3a6c236b2aec5fa3142e1d8d2f067ca520000000000000000000000000b4616c2462cc6470a30c992c634a52258955ed3a000000000000000000000000132f450688513d59df3ba133e8bb0525a693c2440000000000000000000000009547fa331143376c9a22142188ad967ead23fd77000000000000000000000000c212c22caf094f0950fb5f6c097dbd6f13fba3440000000000000000000000003cd5bf238334142a0d52d538c048ee1e7d844f30000000000000000000000000ed0c6f398fd9763e3b872c03947494388981bd74000000000000000000000000d480b3606570d40a908d2232a2e32f53fe3d1855919aea00bc725d700a77c46d969bb946a90c472b07c03808ab1869454e312c67433e621b18d11224143a3b3a43594f6b2ff5db0d628b750d9513992e9cd3df7c3a5cba43bd26f77b0000000000000000000000000000000000000000000000000000000000000000000000001db86855d21df67a8317a5396ffa770a65f9c95b5bfa9d0602833706000000000000000000000000000000000000000000000000000000000000000000000000fe702b44a6fcde4b9826903af09e8e223e64e04cd4868856cbbc1312000000000000000000000000000000000000000000000000000000000000000000000000ffeb9234c9d42e3e9c3e4136195a4d1b618c40370000000000000000000000009b319877ef1f672ea65f1e2b1670da70dc388062000000000000000000000000bdaa5005ef8f981d1883a9392cf74b0ae7c90962000000000000000000000000d65adc470a026a5c57e7ca26f1bfeb6c71177218000000000000000000000000d5fa1571ba7629785de5670f7c15a54b6156345f0000000000000000000000001239f00b3f2efb0a7c5b0f531b3d6c6224d8182700000000000000000000000027cc5a42b8e4c1466a2b826e855a3764fbf102160000000000000000000000007970ee63c07ce939fa7e807248de355cae08cf44000000000000000000000000ed2b491e0fee5376a6b16f78288942320ed35a51e6cd515aa7fc6b04bbfa6b73b52ee22741f6a9079d9a56288671347ac681cd4150178147774ba02281437a2b96b1cc6085e9d25ac3b5b846aad3f9221e61c374cb5b1f70bd946877000000000000000000000000000000000000000000000000000000000000000000000000b71f5b59e54a3a17c766b25c594ce74e76b0630d4600485b56e9a87a0000000000000000000000000000000000000000000000000000000000000000000000006394664a8e75ff07b256de06da9c3d44530a6b11c17c0341a8e3e80f000000000000000000000000000000000000000000000000000000000000000000000000e52bba6c4d970d51e1ccc561694bb56c0e59f360000000000000000000000000fca3135cf527bb20920087440442070793353f0a0000000000000000000000003fd6a542b445aa3cfa215a446aa261323ca61a16000000000000000000000000db421b530d674b752c2d8e79ddeb3d7ec8a8423c000000000000000000000000477380012cbc990228fd17027772d27d56c8a577000000000000000000000000e0a3a425dc964a062a8728324575aa14627d100300000000000000000000000073a8887d4161f11906b6b052717cc52f78d7c163000000000000000000000000c67119213138f3410f03924dbabfbf51a6960352000000000000000000000000fb914c5a0fe5cb20df9f9009a8272d03dcfdef3b22f9be5165d82f61e6556057bd3ee131a3c36a2cd6deb729d8fae635bd6d9a5a550a0525ee051e5784a34f52c6ca7824071e184e4fcf251faf5fac5d12da3d70fc757d342cc95d35000000000000000000000000000000000000000000000000000000000000000000000000ee70ac128ea5aa0308f07946f6a0ef77a591062a0e0366625e75d16b000000000000000000000000000000000000000000000000000000000000000000000000bcbb7636aeba4d76e013f7201225af57feebb41c5f7741715dc98523000000000000000000000000000000000000000000000000000000000000000000000000d761716cb3fcd3461567652a609b8079206bc85a000000000000000000000000be8da1347b1d431f00edb908a3dafe65028ede3300000000000000000000000010532a3157da5d5dc0eda90ccd8ef94ab7d4486f0000000000000000000000006e552a1aab92a722b5a3ff6b1a28a764a619b9650000000000000000000000002a4fbe72ff89a212e84c1a161562985f9f4e9a0000000000000000000000000065bcf1269d82703f3ca57f6c949c6341fac6e63d0000000000000000000000006799eb4a158a620104bfec47789ecc79dd28cb57000000000000000000000000b742ea089b4cc1403cc8d41ed37f5f56c4a29b160000000000000000000000003f4f2326c7277d1d1985f229f2796f29fbccbd23cab6cf4e974cd41ffc76815ebfbba7201551ae5a42495b59c2bab724b022811b6ad69c1006660a5b8baee04524a4172a845c703811ff1224f020a807dfb8986eb75b5661e5155a590000000000000000000000000000000000000000000000000000000000000000000000008f1b287762fedc3a1e154a279306f531ebf35a44f090272f42fac63d000000000000000000000000000000000000000000000000000000000000000000000000dc856973a7a58d23955a4f4866a8cb4b49739b3a45b09228307f2b3a0000000000000000000000000000000000000000000000000000000000000000000000007472523c85df20338ba33c7278194d639148f836000000000000000000000000c626f161a66e6c48dd806762a93abe61ef4de2720000000000000000000000001ca2f44480d7d82717f154699d4cff25bc25e16100000000000000000000000080c1d16c517d3c5fa7eb780c2cdbb418e2d01e54000000000000000000000000209de5726603d87a1610a51de154c059cee962290000000000000000000000001a8c444e9839d440c23665552f7fe45334ceef6700000000000000000000000007969b73d462493413c8a749fe57051dc57f4f55000000000000000000000000f6b8671331c8937d0ac1c6173c702c0931706010000000000000000000000000eeb2465032bcf76e579ae36d8fe8bc1e69d4c311d8f98c7a5dcf3b263cd1080119d7501462a577720c639e5d47cd1d551c7dc178176cdb5d828c77370d4c3f36ae876f5e9ab0375f2aeb991013daf91c8515190467ddd82aa5586851000000000000000000000000000000000000000000000000000000000000000000000000c8051c73d70385079181520efabd12057b89fe7e79bb047c8ff2c04300000000000000000000000000000000000000000000000000000000000000000000000046912d3d4faadc3b44c6345daed26b48eb232d6d1d59ff31198c6663000000000000000000000000000000000000000000000000000000000000000000000000b802f0573a634c6b0fa4792a3ac5af7c7f774d5b00000000000000000000000033499f080ecff96df93b3b2a690d513873c52d590000000000000000000000008f3f8e018c551116a9e94d039b5666785b10512a0000000000000000000000006ac3825a17932b6f76e9886a78531a1b115abe3e000000000000000000000000f7e48a5f4dd49a21d719af15713bca0fc14cc215000000000000000000000000954f383b8c913144e83de154ddd6bd5d9cf6371a000000000000000000000000fe48531ba6b84534738924228ff38d0e585c9408000000000000000000000000a2d17176dd22995f291e2c158eabac59694028170000000000000000000000004ad48b6a1766bf103676ac4eccfea97545e2bc243877e828a881c915bd6f43673508cc7d0ae54d11ef198f64f4401950770b8c5c45d2f604af9e5c538845e20819fe82341b8c7e41c84ff13bd4510804e06006603eac152b2b7c837200000000000000000000000000000000000000000000000000000000000000000000000029093d202c305829f2d6b423cfbfd80ba184e12f0e58203b9702c43e000000000000000000000000000000000000000000000000000000000000000000000000d6ff6e02b576881e589eae7eb1c1ed15e16f564664dcec4c5f27453f000000000000000000000000000000000000000000000000000000000000000000000000b17d2770dc14691b3398da5db6059568879bca1e00000000000000000000000026666a736e769a5421c4b357ef8676125332cb1c0000000000000000000000000bec3e66c4a5063072600333a28e9034fda0f20c0000000000000000000000001629f6705e6892763b89121bda18eb42ff5bd770000000000000000000000000cc1db47e6a6c1a61fdd2ed0cb3e1a7206f3a455f000000000000000000000000fdd2220639de4f0fca6ac8471ff5b748224b0e1b0000000000000000000000004cb971582cf9315de224b50ab6c5e25a6667a6160000000000000000000000003a6ba1566a5ca1192974884c30048f6c4dce0133000000000000000000000000b7ab5c5d3f54530ebfc58c495c97d44c7ccd532fa3e3c4324a67ed63be370d260566464c3b14a642307cb212ed83691eb4327f2f645d3e1f23ae181d19c4ed06b65385643209b734e5acae078d4b9b3eda5b8d71bff36413c3d18473000000000000000000000000000000000000000000000000000000000000000000000000336ed640a033ed77e0927772e9dd652ee601b70881f8e50d6b0d2f4e0000000000000000000000000000000000000000000000000000000000000000000000009f54fa73369b1a2f3be6a930904741395e808250729eba67664a9444000000000000000000000000000000000000000000000000000000000000000000000000ccc4fc19d64e6f0a8c4ed767e1d03110e01bb64100000000000000000000000097673e693ed4c13d5a6afe41d424c43e593e9d0c000000000000000000000000ad3e6134dd63d43e4b01322dd221393a486b802700000000000000000000000000d09931d7c4e855a4762b3eb68bf71cef608353000000000000000000000000b72cd344fbf3f01a6a539e5a8175153f344b890e000000000000000000000000c485020fe52a962e4fcdd52a371c410cc9377022000000000000000000000000d2949c272bc4cf2b2e682f1b3ef6f92e5378bb2e0000000000000000000000001aa1572659183606fd9f525c488ed83769335c6800000000000000000000000065d0c75832c8dc0d0f7d711983778732859e9f2ea97b246c8ca7f95ca636386b522ca6147b1a635869d00a148ae7f505a4858406456f6403415c88380d667a5b56aad829096c7f4f31fb2e45c394342a96140314ae8e73399536f54300000000000000000000000000000000000000000000000000000000000000000000000059b7447213ac5808a8c6c91eb7dc8d73e1d2295da0b19a7b90c3e74a00000000000000000000000000000000000000000000000000000000000000000000000095bab9024dab1b169117416a7f3b6809cbdf233624173543b7a7237d00000000000000000000000000000000000000000000000000000000000000000000000053fc1c7e8ed9720fd0e3c81e33b6fa458c60a40c0000000000000000000000007f0e97379dc45b69fe4fb73af3771736db9b0353000000000000000000000000b102e2301e4ef479cc096646d0d83458254b385f000000000000000000000000958918104b85532eab1cb66465c3e7745c739b2d0000000000000000000000004b254c1ad152f358cc048947d4642a46b7fa730a00000000000000000000000048d23868e824c97b21e6af18b82bdf61ac296d08000000000000000000000000bf6a2e74e04dd52c41e6ec5164b7572eb9408a500000000000000000000000002f1fdb3ab72d5c7c118a8b4e25a34c7ea224c83b000000000000000000000000e7929d7aa328580e4d46602a50f61d66f144ed708e09323b6fc3d14761a18d641b6be33c6df97d33fb994b1b546906773ef4235f6bd2815d059464143b6049270c0495391e28ff7764866f225389db42a4161c460ef6462ff7e9834500000000000000000000000000000000000000000000000000000000000000000000000007196044c475681b68760223c5856257662e0e15eeffc1123738bf2e000000000000000000000000000000000000000000000000000000000000000000000000d82f471a033aa83d9b56aa48d9b92617d225d270b70bc0625ea2413f000000000000000000000000000000000000000000000000000000000000000000000000c4c5580d331df6671ca134653368536495e0267d000000000000000000000000a973944a143741013af78204e49b475733b74517000000000000000000000000523edc0a2a4512560745a866eca2db33cf338b2a0000000000000000000000009a17cd2464fe5b72d6cf5b5ca45ebb37123e32460000000000000000000000003c245c1a4fc044738d737b253c46ad4638926c260000000000000000000000008d0a59653fdeb35c0eafea2022c42828c5631169000000000000000000000000a86ad25def6a993a31c96a2ac0a556092f632f66000000000000000000000000c17ab523563714015b87b4598c35b1670808f63d0000000000000000000000005a438d2526be1f1aa0250219078b6b104f25406567cc1b0e15858d2144b2d94485941c6a3273b34c3306fb7b4bf83c40c4a36352964de372b057a0184ef88b5066099b208087ec4151ea0a1cac537b272bd3d06466f5f239b082793000000000000000000000000000000000000000000000000000000000000000000000000024c5c2167ce3d83d7552523f924b1826f623627a4369fc3d875cd84c000000000000000000000000000000000000000000000000000000000000000000000000cae7b53920f5274b7450d51d49cda219e428d62897427c4fdf4e90190000000000000000000000000000000000000000000000000000000000000000000000003d920c7721e61e0799db804d5f871774830f28010000000000000000000000005361cc1ef6b6033e41d5c66a7b29b276b3a84a400000000000000000000000008fe21c2c98b194479204685b413aea197ebd0e120000000000000000000000003fb19c20d4486c2113710703db37d57ad5d7f162000000000000000000000000460e17477def9d626f31052871142e1788e7b618000000000000000000000000fb67752f35fffa1fb0144168f744113d8585082b00000000000000000000000025ed9362af5ca12b72ff1d25f896232520e7ad150000000000000000000000003aaad81fa36bf55af29b2670dbfd577935ae4850000000000000000000000000760460017962686b56e1f104ebb87f46e2e1810f6af10a425ff4bc1931c9ce509fd9ff1f47a2134047341f091b7a621669c8735095f03118d4ec1e6ad980d23ca512c965f53e41327c24d9634c8f3e468ab63010400d382d19cf936b00000000000000000000000000000000000000000000000000000000000000000000000052e862517acbe65af2456a3174611560b8574f05c86a736ffa7b4c77000000000000000000000000000000000000000000000000000000000000000000000000a5f3cd08fbd85254b538406d730150492e0b3b29a6a0e35a6608e85d000000000000000000000000000000000000000000000000000000000000000000000000cd1647072867ab454f3664443b0a941f8eeee57c00000000000000000000000089f0c4341a36e40e131b764ba2601a0b91688a49000000000000000000000000a3eaaa65da27425553958a59d9fb6055055c726700000000000000000000000025751b40f3c143785dcfc3069125aa4a5c9b964e00000000000000000000000048da67775f3cea1f4d6ee31bd7785f4daa9cd54a000000000000000000000000fb14c1432914205d2949a77a391c1f03da446f1d0000000000000000000000007dbb770d863a4e426be73a2e2409bc2c0cc9a462000000000000000000000000a6960626b2b54525645e366a62535143b298bd2a00000000000000000000000065c7504851df933148cace21b6faef356c837152efc0412d977d392e6c39fb0398e3a609e7cb8c12f44a337c9041bd613fce5e3f67df042f1b3d0109d788ea49e85559445f3d4e50d303b37205f6dd13cab17d049cb8fd5f38f3e239000000000000000000000000000000000000000000000000000000000000000000000000de3b752564283a58d9a12956313a605fc1c14d7b6155941b5cc1c9030000000000000000000000000000000000000000000000000000000000000000000000002e0a8c0ec34fc61367ccdb4998594030c9944e5072acb66d08cbad19000000000000000000000000000000000000000000000000000000000000000000000000cc0ae613338ffa0bca83706e28a6143280284a3a0000000000000000000000004375cf43544a71207ec1a23c5b39d42c26672c14000000000000000000000000c79608550720ec6d486b51715473896e3566613900000000000000000000000076de7204ec85b34b3c923a20ab1fe50130be122e000000000000000000000000cbc480602121bd12ee0be32601a8b543af93182200000000000000000000000072f7650c8f0c92036f228c0acd26070e822da5440000000000000000000000009277f8736cae5408a3e9df0e5048662b2277fe3d000000000000000000000000bbd52b53d6692c1334df0c7df4d6bc17552887440000000000000000000000007b070528859ffa16583936260104a249cac21d3e60444d7440c24e45fc5f6147e1f14840c16447678d050e77e5049d0332461430c087a658c88dad266e9a953d0768bb6977fcdf5fd2de822e62d4d54359f84662f77bb93bb34508370000000000000000000000000000000000000000000000000000000000000000000000006001971bcbb9511b4dbb996baaf5774fa6a135438acb3e4dfafc9f7e000000000000000000000000000000000000000000000000000000000000000000000000ababc60f85e87f279acf99340294130ece52146557a66b24fadcef300000000000000000000000000000000000000000000000000000000000000000000000004c41160b54a41c55abf05257fb1f427960f320790000000000000000000000000d7f031b9f45610ff49ec112138e0c1151b9f0460000000000000000000000003ca10741d2d7df1deba4aa5030427d76b17218520000000000000000000000000a17137e514c6578ddd7107a680f686e1750b37d000000000000000000000000a543042c2f38b419a615524742688e202a41196700000000000000000000000018022027eb076c4324612548a60fe15905050933000000000000000000000000011d236ae436d534d5b5140d9135c2170438173a00000000000000000000000056c3386ba73fa93110a30a3fee22011aff9618290000000000000000000000000b573f71d19d836e8d276965e427fa76fbe21a069d14316444196049b4a4552c01b75e4d9fd6242e1c3c81657b449a30949f1a5629d90b3c057b222ac7c3f67359bde01ebdfd3a7b48909c677c2b43789206615dcdb5383c3464c1170000000000000000000000000000000000000000000000000000000000000000000000002a2a6c3b3279a60c31243b6021038b33d1385a5cd99b9a418b54fb71000000000000000000000000000000000000000000000000000000000000000000000000b7284716549bdc411bfb0e45ab506e4975f7d62dc27d8d2b830e3472000000000000000000000000000000000000000000000000000000000000000000000000ed1b427987231e6940fdbc6c403c4b332167e011000000000000000000000000397ea25db0200c00dab2ff0e01d7272d6f8f280100000000000000000000000016259b06911e2a05467e574d21973341079c4a41000000000000000000000000a0a6102d54d1e85883cad128ea841557d31d737b0000000000000000000000007230614fc947c14c8b68c35e28a60515efe6d444000000000000000000000000075e944c443b3519cab3d71b5c4baf78061d6d58000000000000000000000000902bf36bb0de5d26ebd4f93c4399af6565102226000000000000000000000000ca45c619f855b03c2d9b27462276f463b1201361000000000000000000000000bf594c5f71e33d68b9d3682451bbc15d92d75a1f2114810ec0cee92bcb9bee7c038d5d0280d47205d6186d78406e33202ee09f6835e17442ed37a650c65fe85c7e76f74906aa647b0e92d20d113dfe0c75e9d80ae5cba22d6d34d46a000000000000000000000000000000000000000000000000000000000000000000000000abc5c065fb57586b27710d2526140167cc67b331ccbd724e09aa134500000000000000000000000000000000000000000000000000000000000000000000000093b749572933be6f0e0ffa3e71ee4e0e0d7d8978d7cb03336342ce6d000000000000000000000000000000000000000000000000000000000000000000000000fbd7cf03b2a6a756a9f6335da8e1066ed105ba1b000000000000000000000000dd0a2b1bc154c25434b1e50afc6506339207991a00000000000000000000000074ca5e61bc652f495137205b2c1feb4cebc7ab6700000000000000000000000011315314ba9be33eebb80d1caa88b87564bd2a3d000000000000000000000000bceb5a241938b479a42a4036adeeb10e8370ca120000000000000000000000008983a373e008435c9d0bc728d9930259555e2c1e000000000000000000000000b4d31768752ae9798959e565ebc87d6801a6101200000000000000000000000014873127e6c5941e939c3c35797a430f60f1ec31000000000000000000000000ea94d077918b6f032bb0166543c3803c22568d301b9e567b3d254c4db6ba9110faf70f7901036e1ca722b55ec77af424f79c73286c8dcc4a8ff769627ba5cc52e7e0994daeae394172d7c8147dc35960a688c01debeabb1728310b2100000000000000000000000000000000000000000000000000000000000000000000000013592e512faef05ed8dd5b43b5dcb37d6c136a560b2dc154b5021d080000000000000000000000000000000000000000000000000000000000000000000000008332c47d6b02b465c350ef0cb742593dd4a6b25607848e7083b5dc6e000000000000000000000000000000000000000000000000000000000000000000000000de2b352a917f95585c1a9f7a5fdf3f59f445a82f0000000000000000000000001251ed64f71616256f8e6d19db7d1b5a92693f340000000000000000000000009b886a4b095ebd56d2943a1fe64597056e846774000000000000000000000000f54fe62c971ef538a10c7e264387e905ced1cc5d000000000000000000000000e01fdd08fe569768c398ce3f83da117d93d8fe380000000000000000000000009f3c231d116dea1506cc8e6f3fb0c240954a066e0000000000000000000000004fd04c6449612e28b2f8e72a95d279785fc3324a000000000000000000000000365c814e74a09c66a6e46e6f5d0af475bdcff9420000000000000000000000003a5e804778cc6472bd1b276b6ca44612ed081e3ebee61938a3bb670a67ebd023cb61075844ba3b59c780cd0dd6f9210eb72bb21bc8138a0578af9618b6cc1a32cf10cf408fe9f45c73df4d627bad36191b7daf725257ad1b58036735000000000000000000000000000000000000000000000000000000000000000000000000dadb0636e949b332210bcd1a0e605d6e048d3c3612a711374c273e61000000000000000000000000000000000000000000000000000000000000000000000000df9dc03c6b48e0585b06912a86720c025d484e42539a3b4280be4b6c000000000000000000000000000000000000000000000000000000000000000000000000f73a5d3cbd66e629125b025f95fdd33fb3880848000000000000000000000000582d6e3efcafbd0ab2213a7d9228e530e0e86c56000000000000000000000000b7953672660328182c024e7b1a28a1171350d802000000000000000000000000d7f6947738af326c2a3569292bfad96bb200bf58000000000000000000000000ce663444592c7414cce14d2b904f3504c080af270000000000000000000000009118f92aba9c14280c4d7d526cd1e466717f5e6e0000000000000000000000007cd49d4315c1041470d71e4f1b7457648fc59e0d00000000000000000000000040032146df0c741941bc1220d3a1ca53eb97fa16000000000000000000000000a13e5e74939a2446596562656cd00815e932e6240d8d0d1dffd4b8463414cf5261be0f47d6795529ef7e0c57f1cbf24fd64db828a5be3d18d7a31e75eeb3810bee3ce5523414cc5175bce85c63e3802e331b3f71190e4b6e028c754a0000000000000000000000000000000000000000000000000000000000000000000000008ce6c325635fe35baa7390211bb2506777e57b3f55b0f565b0dce36400000000000000000000000000000000000000000000000000000000000000000000000048a9f02f557f2d6f08cb407bfa091269abf3237dd685bd190ffb5e340000000000000000000000000000000000000000000000000000000000000000000000002db7ea236d5c4e7b209282027333e05205cf103f000000000000000000000000c5e189160522f2664d7ccc1d4fe9b37684312741000000000000000000000000ad9bdb787128ee3d3069ca6f6f1ba74b89c6d0050000000000000000000000000c16d5453784fd5e36a56f4b6b91bc11ffa747650000000000000000000000005ed4ff2ddc75085aa75365753fa6b258fc8fd104000000000000000000000000a7d119682d4e9962d74d2c32e2a74d33b9f90456000000000000000000000000a3fdc9338cc59b13a1def05c12dae469b053730b000000000000000000000000f6af904f0826af6cd95c7d07ae57936a569ab60800000000000000000000000050b4ea19e816fd27465ca243599212230e68991477fe071683d3a2534d8b3a1c10a2956fd91092387f34763c7223fe384bae0f762c9ba41e08b42a06c27fd9584a2d5e05c0d5da2619e2703672c74440ba495a1fdf14a9325ae3513e0000000000000000000000000000000000000000000000000000000000000000000000000f55c8380a3c541971886d1bd0e0341fdeab57212c9aeb02f97e59300000000000000000000000000000000000000000000000000000000000000000000000001f2fe877a5c65a07a6b957528f8e6a0693004c1257e6fb2b2b6bc27c000000000000000000000000000000000000000000000000000000000000000000000000c56cd14d8f68ed4cd3a2567b66fc5654e40db216000000000000000000000000bc5a8469d5fac642330d18184614ee15e17b2347000000000000000000000000f311d933aabd7959eb8ceb19a320a6613cf6255b000000000000000000000000358472382356c403d7eff10223f4856c9fad76610000000000000000000000009a2ab61a8d485e62b6c4f44b4be0854b9cd23c310000000000000000000000005a46417bb5f11c317f16f672049e507838d4cc6d000000000000000000000000b342556bfe9eec5ceecb4a5b7c0f3c77d09f5540000000000000000000000000b3951c665d8aba0ca8b3d82c84ac360f9621ed2900000000000000000000000062ad9f79321b8746732d1b0dbbecd65c98b5945d8a037f63cb72de499f058b47d065bf33691b647199510173465b065af19c252f3154ca759d289118333d1e11bb57bd71af86026614fcad7619b3a64e25b1ab59f5def52ba57c9e05000000000000000000000000000000000000000000000000000000000000000000000000afbfb43df8f7f16e698cfc344e59953a8243876931a0d21d3d96ec0e00000000000000000000000000000000000000000000000000000000000000000000000085593959047fee3ccfe8905f9ad8cf229365f61ed8cc8d4175d8fe2d00000000000000000000000000000000000000000000000000000000000000000000000078484b3172c34a3391aede02c6463779a833de77000000000000000000000000b232e324a67a3b2743307e5c4d23712575da2951000000000000000000000000f869b37ef05b633a90aaa41ef021ed1b1f86313c000000000000000000000000b394c171cf11717e4b303349d950a86a2c0b6d750000000000000000000000009186b00a59a0811da1ae66210764377886658416000000000000000000000000330b837ebb953b1a08c8bb32e960bb3225637703000000000000000000000000f5aeb35f1a11f10652acbd1a7496fb6faffb557e000000000000000000000000fcbb60025e51a06457630218fbef9925d233f34f0000000000000000000000002338485a8a415f3c61b6103e82947f2f309f840300249405ea1035646b8bf81400664a52e8f2975a75613525c2aa7c0c79ea9157c09d4d4f6d8fb025e0631f77cfba416790e51708776b077630cd7b56265082088e293c17aedd023b00000000000000000000000000000000000000000000000000000000000000000000000088250e064c008546ddf21b68af89c24de82ec902aeea5d4b9bfa527b00000000000000000000000000000000000000000000000000000000000000000000000037701c76c16eb12cdf1dd54dd9c92a32fbbb9f642687ee7a5995d632000000000000000000000000000000000000000000000000000000000000000000000000cf3f1170ceca497af892672bc4a451213f3fa127000000000000000000000000d8478d704a55375449d87a341603097aaf857036000000000000000000000000c52eae3147fd902db2527f34dc9c237db935dd24000000000000000000000000ae840252cfcd282b993b316c8aca3b41fde04620000000000000000000000000f80cd76f4c2f915a2a3a0858e32f97733cfd316e000000000000000000000000b5a45e1626817759e866ee40a05f3b5879da7955000000000000000000000000010bd477a20fce789c9419236126067575697f1d00000000000000000000000076ccf6601b88f50c6ad6bb3a8d651a50a8740a110000000000000000000000009178014163a8ff22016eaf1dbc1fed403c23e37b3690f8643085372c6d39a55fa9b35341f98a4247e2e8406624d8f727c4028248e1f63612f7a53b0de6cd8520b224b32db7f2030b38055c309391bc32bfd0ca2da068ee7689a1bc7a00000000000000000000000000000000000000000000000000000000000000000000000003449b4929bb2a0ebc3420326879362aa66835525dae0137ba6678720000000000000000000000000000000000000000000000000000000000000000000000002cfb937239b05028fc761d67d6050f778c83a10159583d6cb837266000000000000000000000000000000000000000000000000000000000000000000000000058c71d362177c40e61e0ab1ea59e3d7e0d887262000000000000000000000000e7ab650874cc937e8bacb4374c456f5a84924b44000000000000000000000000044bb61c7dbb04493a8d2d7ae2dad4750b8a8d5b000000000000000000000000b9d5b825379d605e4d3d68402ca9b755d3fc6b79000000000000000000000000a52da8196e8bea7470090841cb9ec7182aa2b010000000000000000000000000a46dee467a53994e6058a7566ab70f7c9d3db4370000000000000000000000002816d80727e67148bd344d1afbd34132b0eee0500000000000000000000000005002234217410f635d9df643d41a1a7a7791f414000000000000000000000000e1b9b5371818562347877e64ee14b77da52ab960ac42a168d7a38103194f695dd151413ab0b40a5d4b8de254d650ca16f05f305994a32007df644920f86fbc595d64694c49b5d04570943c17368eef38e5eb0209971cce0e28bb0420000000000000000000000000000000000000000000000000000000000000000000000000f8a652789f4667400d21740c27df23072e023f07efd340114aa2326800000000000000000000000000000000000000000000000000000000000000000000000083537c381e2c481463265c3d36153a4e78864b0a7e4a4f75fc50a52c00000000000000000000000000000000000000000000000000000000000000000000000006b7bc74d646241992bc34173f4c5552de78db3d000000000000000000000000cc35b0214873c20e8682c81b1a11c213a671c7520000000000000000000000002d4cf437d78c3037c632bb18aada692e724974400000000000000000000000000a016c615a5f5b1d07ae1411b7988152efebf55e000000000000000000000000d5189c6f016ff119f49054482707564c9b00e629000000000000000000000000097bdb5f930f07293f49921dfecfca5bcf9a3a130000000000000000000000000c0c723b23c67b4008d9f6469dd4ed2397fef45e000000000000000000000000a20c180a6c779d06cd96f34bbe915d3570a1b52c0000000000000000000000003ca5924d1129d84a38855105ebc38269304a8848eb711551485399155d66ee36bd9d4e75c9157222128c075228bbbb284f9ec26a2e5e7c0f49351f227ebe3c57a6fd234e14e51d066dbf4800942561319a35c72d1c0ddb131b7b054900000000000000000000000000000000000000000000000000000000000000000000000024daf877141d4e5560a67742ffc99f760838167e01d0975909614b00000000000000000000000000000000000000000000000000000000000000000000000000d39c235a2d47f92226819059da8e2d7d08929b54cf1bb25c9057cd6c0000000000000000000000000000000000000000000000000000000000000000000000008c54be056c67da799ff5303cf6e87a4707bef86d00000000000000000000000049a28337da96ef2e3949737bf563de61679c1e5300000000000000000000000044d03f3d142dbe521885f669a0593c053c1a0e080000000000000000000000000ecfc17bf0af01170f91830b715f3a4dc14348310000000000000000000000003fc5ca406dc7cd7477dae83a945202381475a50c000000000000000000000000c0dc22475f9b536be1dea247d12b046033f7d678000000000000000000000000d6554928d7ae4c4149deb3787f13cd368dd3490f00000000000000000000000055c07c7384c0e42066c7e726d3995a389e30582e000000000000000000000000062db13c7dc4f176185320205b6efc58181d7e141c76f51bcaa3af2bc8cf603cc92078065fd82d35c2cb6f222dc19f65d2a47113d953f1622486005fd786fa55dd15a4595cc55e769d2c8725acd763364b74912a5797a81fdafb7850000000000000000000000000000000000000000000000000000000000000000000000000820c8d7c44bfcf095859732ad5e1042a2d792d14729f1a5b778b954100000000000000000000000000000000000000000000000000000000000000000000000091b78e414e694b22ab7f582a4ab7c6374cb8631152aa927ea427482b0000000000000000000000000000000000000000000000000000000000000000000000005f02a22752cfe83fb0e99b027aee7a22d4be9d4f0000000000000000000000004fed1153f740065096854377cc15781c5f89913d0000000000000000000000006837021b703eb434d4d3303ac54da925d4ecda4500000000000000000000000008e463393ade3e168105e7244e577f2af41da77d000000000000000000000000b28cca530407c8157f198d52f100246c4c8493480000000000000000000000004688222b46a62b62e9ba87502fa93c01c140994200000000000000000000000017753d482d2f8e6ac1d6f81207c73646507ca0080000000000000000000000004b418d791cb5727735dab254e9707836b6bd5e73000000000000000000000000caaca725d72b737dfa5d457e5c13ad5ff5a9880508d7525ef9d539069012c6283a27d1175eb43b6ff2c5a22460ad0643d57b1f488271e77c64727c592ecef9333afe99781b45263e37477d1e20a7ea3485821e0b02bb1676204543730000000000000000000000000000000000000000000000000000000000000000000000002e535a592de8b854271d4851926c2c017b19582e9c7d80028f5dae550000000000000000000000000000000000000000000000000000000000000000000000001a2d9c0cd92b7d2c71ae5833ceeb095b6b4ca457ad31a04a3fcf9f4300000000000000000000000000000000000000000000000000000000000000000000000004793d3ce882254bd3da2232b6c16165f745a96f0000000000000000000000004a7a7013c338081e0076d85176c9431eb9150b5b00000000000000000000000000764d68b505440799db406c520f3d4072d5283400000000000000000000000027daca3c25860d19bface22d338d2d045e2b2c75000000000000000000000000b468ea74499ad57166cb215530d176247d3821710000000000000000000000006662dc45375732032e6bf322b229fa68865f0d0e0000000000000000000000003c23445984baf7159748390ceed2331334d5315e000000000000000000000000eb4fdf3a13be9441ed7d7e3040c6d07d17560365000000000000000000000000645f7806140958217a98247d03ce951aaa7afe564a4ad577cdbfd1298b5733544468bb14dc78e83e0fafea53dd0ca3447240567d6a50b9193a070019cd1b8260018b7325673bb365cda7d710b60b5f71b775015971e9097d7022a606000000000000000000000000000000000000000000000000000000000000000000000000594faf782d419f0229d0e847fb8dcd6c119f5a7d02bea75455f95166000000000000000000000000000000000000000000000000000000000000000000000000e0f6511e0b9a616d11eb7c0cd7086064d8913d659143d16f11facb6600000000000000000000000000000000000000000000000000000000000000000000000093b7ea3df8773c1b31807f0681242a1164b3e4580000000000000000000000002fe179100b1ec719780a8628de137a135511285100000000000000000000000024a8ae2821e61349a0e8674353daff5b3167387d000000000000000000000000841b024efee98c2544e9c911a88996265003642d000000000000000000000000dcb9f90d87bea0421852c03191b4c3761bf0451e0000000000000000000000009a1bc01633879828f6c2b538a6cb9035ecc7092a000000000000000000000000f8b92d4680c48671a4138326377f0a0ebda10558000000000000000000000000c26e8e44140df108a36e1d4ddfe216542422ee5f0000000000000000000000008205e1565156712f560b4b7550d65c57623637636793196508b188544fe8f72dce222513fdd77a7e7b6caf0609c3bc05ac133453ad31da006ea84e685d1f6c27047e03566ca7ba7b4ad87d17ae64467c8d247f7081b2b2566ae6532f0000000000000000000000000000000000000000000000000000000000000000000000003def416860efc751d0d8732b740f743ef1fe8361caa94737bb67862d0000000000000000000000000000000000000000000000000000000000000000000000009eb787510c173d5336b8de0230ac5a5427d7f33200ca9524c7dd252000000000000000000000000000000000000000000000000000000000000000000000000014fa583332e2680db6fd365270521760f513b3560000000000000000000000004766d402c9f58b2bb9baf50e4db0294bd39bf842000000000000000000000000766497670d64db25fe375d7908de0d04483f440100000000000000000000000047321d4915dcbf19b386d5306975f35141230155000000000000000000000000cee14b2bf6f9a5616e34810a98a82b53a9eb4e1f0000000000000000000000001e1e096de7bf0c5f2fea662367d398285300362600000000000000000000000030bbe0673a10e4202704e37346e4332dbb210c74000000000000000000000000b118184a96bf3e18109d8a47037190264606c1200000000000000000000000007e69b1173d1fcc1b1b83db1aae1731201ec795044455cc14f6778d4245e48a67031f00579632406a93ef2629115ed91a37f13757ee47f6395a9dc33004f6f6464bead5742d881359da6a3365fa78285acea63c6945bfe20564911466000000000000000000000000000000000000000000000000000000000000000000000000186e13232bf3cd34c68f0d57902b6f5d45ba474a78e25d103a99e9630000000000000000000000000000000000000000000000000000000000000000000000000f9a903210bce616290af06935edd45274875a7c4803db0333b9136f000000000000000000000000000000000000000000000000000000000000000000000000e822f6169b2cb17c68dc212892d4ce40acf0db750000000000000000000000009bf5f723cc50d0452a66da588d662a3af0e4545200000000000000000000000042d0211cb9abc65b616dc80de380b27b4406d7210000000000000000000000004837d51617039339855ad700de8f446cbc1c4a42000000000000000000000000231ebc030981ab7bfe3add4511200735bf0c155700000000000000000000000018556037ea73e04d7c88790b38f09d13dcf6546c0000000000000000000000008abfc0666b616d647333c4522ec30c04465041460000000000000000000000000ce55d090a25f31058722357bd9bb5612ce0906c000000000000000000000000dfc08258f2b0da164c66e07691b9061456231a73331d0e7764778b69c0df107440ad1a04dea3fd5c8ac28659335f616304cba605abe3470d04c5854afa381226044bfb60fe113d6feec8381782a7ba4a270448172f05bb2f29d97b040000000000000000000000000000000000000000000000000000000000000000000000003aed900b9a991f64486c22767fada36d852870783eb4662179b5b964000000000000000000000000000000000000000000000000000000000000000000000000f9908f6e4d45a513712a206a8479806d777cd57e5b63255915592615000000000000000000000000000000000000000000000000000000000000000000000000dd2d534894cb551fa502262d37d7f04ef6ff682b0000000000000000000000005ee4f371d2b23d1896a6d9507ef29021eb910c3000000000000000000000000065278d02c9fd01759a55817e3cfaba4f3ef1340d000000000000000000000000669b2833da905f6281ee975df4f5fe1c1b4b776500000000000000000000000027a6c202dda78c112c9e334e33e2ed236350e31c000000000000000000000000b18fcf625fe98072206a0f548187250fb0727f42000000000000000000000000f4edf94d968aed38951aec739f6aa94397f43a030000000000000000000000004cdec57566956c6e1a74284043774f0d953cb64b000000000000000000000000e338010b0f57230c534f87737d6a684eb95c765ceaa4e845ed8ba65e7a93cb3426621129bf3c7509469cfa0729d49c40b8614f25586e747b3edf510621dc78548b490a3e449e9e2fa7b40a6721886034cfd2062dc7744b12802e8454000000000000000000000000000000000000000000000000000000000000000000000000db6a192d57206321cd006e5e05164d58b61a1917c05d7b668b3b6061000000000000000000000000000000000000000000000000000000000000000000000000cce53b17b982e63b021b8f7de6d49f3f1026ab68029ead085f17a61f00000000000000000000000000000000000000000000000000000000000000000000000021410c3348180951360088784839e2597cc4ef4600000000000000000000000079f2081eddc9686cc54e5745d0886c7cf28b8d0c00000000000000000000000017d45f103655757a6e8f030b50c2ea4cf23eaa200000000000000000000000004e5266475930ac5604e2aa381455ed7df14367450000000000000000000000008978d86aadfd574f69f5347e3fffa52aeef43a6100000000000000000000000026000926b4fa744ae9989c518a02e64472faa30b000000000000000000000000544f720b54ee2b5e820a883580baba108a3029570000000000000000000000002eade543c169e04b2d711a39d11c656b06478503000000000000000000000000b8aaee51f866410af1577f733474875ac217f67d99e39441a9655e3c984f741a2c6405164104795218941d4d4a61d65a79442c6c1f88732e0478dc02d6e1601b3b03d938ebffa77b093ad52a76cc1953c8b982433ca9c1685292fa37000000000000000000000000000000000000000000000000000000000000000000000000996cbb5330380759380c9a2dc39a8d49d42b797e6a34e51d8e99792c0000000000000000000000000000000000000000000000000000000000000000000000003688c127ff963929e7c87020be5a603ad127dd44d7f32647a5ad696a000000000000000000000000000000000000000000000000000000000000000000000000e780e3528e8df4563c94cd368cc2274fd5391f0a000000000000000000000000497efd2c65b7ac1ae4479c4a7ef84c3ebf57f1510000000000000000000000000a56e90224ed016e398ca271aa7edd252f52df2d000000000000000000000000962ec94ccc503a290ec55557ee50f745bee6a7570000000000000000000000006aa1913092d61e702a127a02ac8fb2521bdd3a7c00000000000000000000000044baed600b2d88702b848015e102526ce3cdae420000000000000000000000007d92c91f22a91329616aa520374d1a36f35fa7160000000000000000000000001cada35b44456751ba6d287b46446e4d7c0e666e0000000000000000000000005361da5273d7767c9fac0a46af9ed00bfe65784f6cf92440c06b57736d3a7b4da2000444e8486168640eda284f913169008273642c80bf4125df2815b6bcf0299dde6f1eeaf0647079d487592da1545d3565ed66cf9d592fed302a5300000000000000000000000000000000000000000000000000000000000000000000000095e9ec338c07b06d12923435fffe65242bdcd639ce22d611f3835112000000000000000000000000000000000000000000000000000000000000000000000000827c361f7939b1625b0485579219327782edf05e94f2f13f4694c82c00000000000000000000000000000000000000000000000000000000000000000000000019979468766acd09d49b2e11751ae85ee2a8972d00000000000000000000000032acc61a7135bd496a384e3c92d34e2af7486a25000000000000000000000000493136021238b07eff877a0e8c01f12e10300e56000000000000000000000000fcd76b5e7b3ccf518fd2c06798995a299b058d6400000000000000000000000094b33d532315f0323851c22b72f35408e2af7939000000000000000000000000191890218ea9f31443ea3951f5fcf2559e33bd65000000000000000000000000b73a1d67f63d4054225b450cedea5c44de171b4f00000000000000000000000028ceaf6bd7e4e154ae989b7596f4d03d2c2e0a08000000000000000000000000e8e3e37414a43c0717b5610fe2eeff65df0e15576851817aa1195332e44cb51f48c2b16fe565f0227d647657d6dfe13ab0f4835b3a46072ef4996c0fc171ba1944254f1617f8156d33c1d81d88265b41c14b4259e2ee536d330d34130000000000000000000000000000000000000000000000000000000000000000000000005fbfb2482bc32f0cc8802b4fea1d96534aa54a5d9264865f82d4594000000000000000000000000000000000000000000000000000000000000000000000000031e8e75a4911925b3a4c5b75b9cd332895ad571fc15ff637d330196b000000000000000000000000000000000000000000000000000000000000000000000000629e507b14f38d2eeeb6004420ac877c5da69f680000000000000000000000004165830eb07e3872d1bbff1cc8a2281ba8e557000000000000000000000000007c865d562fa3b12598a22c3914cba71e1a8c2f6000000000000000000000000056bad3009ce37519e236bd3a66487415187a325b000000000000000000000000b4243e0448262b7d7affac37bd5c091a33fffe4f000000000000000000000000d4766033dc30697d87788d1c656a9961a25c5e1d0000000000000000000000007b593536a6effa6b26296678ffd36e38eb07eb7d00000000000000000000000094425f344dd92d4e7da0d91a952aec09fda7ea3900000000000000000000000008875242f72ee930340e427df45f232d0ada8c7d5ba8dd2a82406d41caaaa050613cc5631e2ce132b885de5816bc0825d865b373721e1278b338dd000eb9ce219bf5a36f8a0bca633d02875628a07f11bed39e44bcd8425eea93434e000000000000000000000000000000000000000000000000000000000000000000000000d774595dba4fbc684ed2c10522adb7715fa3434b9710581d0417055800000000000000000000000000000000000000000000000000000000000000000000000084870a3ee9c0e7575b0bac0e9f324101b1d8330c07b2484d6ebf17710000000000000000000000000000000000000000000000000000000000000000000000004b144e523a726b3da93d5e4aec698253d3fb3f370000000000000000000000000d813f61ffbc9b4a9c74023d2b0e2f190936ef52000000000000000000000000f35d1620b02ee348880cd448ec4332521846a3650000000000000000000000002b573f4965b2cf243d5d74651524533747048c55000000000000000000000000afab332eaf83576bcd9fd068380d216fd0e04b6e0000000000000000000000003e07a778f329742e80ab3e4fb8432d153ddc1f120000000000000000000000006374237eeb562e02c069bf67f803036cfe9442250000000000000000000000000262770c1fb8366606727f26e06d4e05ba6a771700000000000000000000000057854512f0899d732681b8752a693007a6496f0ed5776226524d486f47bdc71bff182c6ac75042365106b13692c0a56e75bad3562c024c23ee15640fe1a22e32b660811940ec6d2c5d8174689d27041f63fe936a4c8b14169814345b0000000000000000000000000000000000000000000000000000000000000000000000000989c4769b841f270dba3d1cad87a82aab362f06f71a4c20dc5ecb5800000000000000000000000000000000000000000000000000000000000000000000000076479759b562bf68ba36e13d7de08a09a6c328171475e60c13a0461300000000000000000000000000000000000000000000000000000000000000000000000027219047c6da6457a8e0e74e0bf68664399e3068000000000000000000000000eeab1e5aed12ac01a5861004766843000e9e476f000000000000000000000000fe4a4171d656690e63790f4ce8341a2e772e2d7b00000000000000000000000046cdde2758a1b518e75196372cd7d05c89a4d7170000000000000000000000002a2c883b8c0990039f54af07d1220f1f2aedcb0f000000000000000000000000cfe7572e33a9811d3b47f616c4c7e46c77251d0a0000000000000000000000005de05e0301a3be31dd0fdc1eaf236d45b202e25e00000000000000000000000001502a218fb6c91d458cfc20ab1ffe4cb543985d000000000000000000000000956c8d2952eff1153365bb2f28a7f5667cfe3251808af2614165370777512c1da8dfe327aaebe92b5b8ae47b3648136f2bba1401f4d96f3f0677ab5a92b677670dafc3118dd7e00269928c2716e6767561c9bc11216e3410e37d215a0000000000000000000000000000000000000000000000000000000000000000000000006092044c33dad80d81dbf5435ffbbc1c2b0588723ca43e78d15e0479000000000000000000000000000000000000000000000000000000000000000000000000f0c8bf6af0396a2d0382033b70f6fa2090131035dc6bc102903be3620000000000000000000000000000000000000000000000000000000000000000000000002846c2440e50ab41567ce460c2d60a12facbf90b000000000000000000000000908cb37748288d7e40a44f085deea6695e2c905b00000000000000000000000006a54504af286343e7a3dd7961ced51a7ed18511000000000000000000000000fcb27f524ef0a05ccad9f41569981b3a5d60e747000000000000000000000000c1149c11b0a74a43173df93559c1f508584a644500000000000000000000000045d97078a095be06ff747b0abd46ff532064ab47000000000000000000000000a130e173d722923c6d936e21cb02587565d14273000000000000000000000000a1895b664ce5100a9dcced18dcc5c8267284454a0000000000000000000000002210f22759cde31680e21b545d55c93011fa35328d956e4951dd3843954a3171a2f48215ff416f3476708913aeb80843b88e664c535d43113d82372ececf092613a6742b1dec75287a4bb46c076c05441038ca669f815e77d5c599190000000000000000000000000000000000000000000000000000000000000000000000001562802ed8f5524d78dd3a2eb7ed784c4330b6406e5ee443f473e925000000000000000000000000000000000000000000000000000000000000000000000000bad0986637137539940bd80225bb5438cc9d3769ac8e3504f43fd56d000000000000000000000000000000000000000000000000000000000000000000000000ac7bc950f51e6852b860cd5d51ef277e965d5b290000000000000000000000004618da4a329ec848d7ee7e0eab85d1587b78c83000000000000000000000000095653c39f54bcc09673c5e592819a66590729d430000000000000000000000002b2c64588de36712bcd04839287f387310894443000000000000000000000000fdf8e84f32346a50f3fa1209c8f785379651435d0000000000000000000000002c9d5d758945a11e0b09ee10f7fb3e5fb940a35d000000000000000000000000f8e6cf1a9ead373c68879f5aa931861c8a3e333d0000000000000000000000007ffcfe650e4449585d3545693cd115527615ed480000000000000000000000007687a81ad17ee87383cfb3146f59235026ab65594e47205fc9c3d373e92b56473c20ac57b0a645033ade3e05a0a88d4fc77a1550d4527d10d019346e7b04fd45ee19d81b202d8f6e87987427ae049a31d30f9436f52589682f3ae635000000000000000000000000000000000000000000000000000000000000000000000000c75f043397c3b07a62d7b125f4624d064650f47234db9548bdbf0c21000000000000000000000000000000000000000000000000000000000000000000000000692b270603fd372b8d527e2ef7352c54a64b76699cee9d5f55c28b14000000000000000000000000000000000000000000000000000000000000000000000000f5fc0d375382ee1c37e6e55aa151a563c29ff7150000000000000000000000003cc5cd7ca6da2122c4b6035c64383717e2718069000000000000000000000000743f1e4c4212ab6c9f9a1a39519d1a0c41b3dc31000000000000000000000000fdaabc2f0da9ab049a19597a47b3571499858e68000000000000000000000000980e047aa9fb132ab463043119ee5459045e253500000000000000000000000060e83c05e303e52fe92daf736c1faa17f8f00656000000000000000000000000167cbc63bff91472764c516a4715dd52ee94534b0000000000000000000000006ce1af0e0db11a3280b8fb4b48da430dbd9b096d00000000000000000000000001d5261c8ea8ee61460629442b2c4275a2bd3228b2456d793e706a541b4c4f1447be7764c20f96385385ea3c2943d67386325a4d1827fa39c4ea3d44fffee8401b47000859602b46c47e857d0c7e8e440c68624b797c002a5a4e1c5c0000000000000000000000000000000000000000000000000000000000000000000000009e9b61289b56c477448d5277de06b537c11e4575efd9920e4f06915a000000000000000000000000000000000000000000000000000000000000000000000000b9c99f10baec165c2d5f5c182898b275f3d60d6d8cdf326c69cb2016000000000000000000000000000000000000000000000000000000000000000000000000ca846d2a96f8f05487a1fb2a725e635f10b4cf3a0000000000000000000000005961f60fc2d671429f8a40014af15a7b0de76b6f00000000000000000000000059db3c2c122163646b5f4a6ecf20961d8f9156660000000000000000000000004b4a3869e9f03b17e2fc0d03770fc53fa91b0c0c000000000000000000000000a385645bb5302605e5f0a322b9370c58c9cf4654000000000000000000000000a089a72b2caadf0acd954251caac1c2051758063000000000000000000000000d9acf457c92ee264bf90003194db1112fdffae6d00000000000000000000000003e9002fe4272367951d246867194e7bcb6e272c000000000000000000000000c5114a67e962fb424dfa1c7b0a110b49955a4367dfe21223fc5f4f4f4ff5544f137a82373a933d3d02ef0d69f9241c15c957d63a0c96a30bfdf5652dab44c864595be628cfd56d6c2d229464dfb1fe2e8136f163d5e8f25375b8be0c000000000000000000000000000000000000000000000000000000000000000000000000df4cb86b060445333284db0bc177990a997a2d3af58d0514869b00560000000000000000000000000000000000000000000000000000000000000000000000006dfd72656a4cf24a2e69aa28c331dc5d8739c33e91805c1f6f60fa6500000000000000000000000000000000000000000000000000000000000000000000000097d6455b0401cc34da88d06c7e880628cd59210800000000000000000000000040e9983d6fb46153980ab378a5f794455f5df01c00000000000000000000000038ab333b244afc32a94a212064f0c4013cf7b958000000000000000000000000f13ff91a61dc3d0ec9e83d0cbc869243bec2b21f000000000000000000000000caee344bbe4f752d0038065af5dc893613291f29000000000000000000000000f3a81230b078cf24baa1ba3740ad276cd442bb14000000000000000000000000a878bd6479652532ada1ea305e0d074c0c29f920000000000000000000000000211cc73ae5e48833ac084a55481e6122201a7d7c000000000000000000000000238f610f526a2c5ba2641f2f8af58167fd182d527513d11903fd71052b71e818d27f280b47d214551ab3ed5292a1103b6b853a39e1e0096709476a576d26cc7bfe660f6b3ebd971a5ca7002498a8dd2ba4622945e21540564f944b6600000000000000000000000000000000000000000000000000000000000000000000000048e67008210f6e162753813581c69c24369d6b769045792d8458934f000000000000000000000000000000000000000000000000000000000000000000000000cdab0a57178c7c53e7641b0c9fe00a35fcbfd80fe715a97ca0451b330000000000000000000000000000000000000000000000000000000000000000000000005e83cf650686e150a99e6d74b2f38b72cc1c5629000000000000000000000000d8960d1c29c4ac405565c514f52c2621e5ba972b000000000000000000000000b51d1414fbfa686bbfcc1c7357d3d84a282df4710000000000000000000000007aaf6a65b3613c67fded823f671a091c2b4dc91900000000000000000000000051ab0b422061cb73b71fa76359768c5cea27195c000000000000000000000000e80623517bd53c587fac5c6b0407cb6eda53f111000000000000000000000000b7ebf05b1c3a9355cadfce0b068c8c16f28f3d01000000000000000000000000a765f9190825610c7d8b6034eb5fa324b39eb43900000000000000000000000046d62f13d6acf35c7b41481ea6f3036dec509212234ebd46e1f4e9660e91315543b23905cd2355578ec4dd32a80d301b45b64e1f219e270b06714006db829d0fedf6b16b9d17127b41b7172261b45d19e209c35990698e3fad6f8e78000000000000000000000000000000000000000000000000000000000000000000000000338ed565c66e851da760f65888baf345a75f5c4a2c18b366a8e23e28000000000000000000000000000000000000000000000000000000000000000000000000101cf46a22781d587b47d019264f9a5d9ec2441ba6d50e3835bd6d1b0000000000000000000000000000000000000000000000000000000000000000000000008a2a44190eb457451d2e2e2fa5895b4ed01d8065000000000000000000000000f7987555b1441175865c862f0ba4704bae103368000000000000000000000000578f8562cd2b63233c158e7173f8c3006aa7bd0a0000000000000000000000000fc9352352ed5a29b52e546051196d3615c07313000000000000000000000000a53502144df2561866511e06b506ae476acf522700000000000000000000000076df9d5c09eceb32ed6fc479ef1e5d3507b84d13000000000000000000000000b8f4b71fc0426d719c7475529001e365612af14b000000000000000000000000e8a8617e50c8e96ce00e2e6c8e0d1b080750fa20000000000000000000000000f1bd132d1a1d3e60c1a85b62a3cfad5f61732b40aa427f3d8ecfb84e15eadb2b7b5aa56fd46e185ed4027e71c7da0338aceab62e7e7d3c3c59237327bf4742024e4ac21b992a094593229e18d65ad868ba070e3e46fbca7e83e59401000000000000000000000000000000000000000000000000000000000000000000000000dca138679fd3bd3a4f50954b1b83b528df70b22324b1125c7a35f1000000000000000000000000000000000000000000000000000000000000000000000000007320505923b299136b7f095e92523604ae667e42739f253688206f4b000000000000000000000000000000000000000000000000000000000000000000000000269e2b6eefbba55151a158033bd1a9302f8a16560000000000000000000000006807be12e3a3b724b4de70552452cb6094c9b9330000000000000000000000002d11a25edf6221564492b100975128169078af1d000000000000000000000000617e083ac4b2b54601dda47b25e34971c0f94509000000000000000000000000859b77432c223912b2ac1c60f89cf55664b58f1a000000000000000000000000a7914740e4f5146b465381108c23da23d8df3f4c000000000000000000000000c576e00f5825ab0ce6709f52e2f45f1c1420f96a0000000000000000000000006e8ae12b0a465409c4a0fe588d0b8557dc16863e00000000000000000000000065cb1f0117931f424251003c6615995769a96076bab0120eddca4b0229ec637ddcd4cf1868a81877ad35e436f7c7c34f6d018d262fdc921d3a6201477731b77ef506861674a6951740e3580c26404621422a1a16a47abd646d99543d00000000000000000000000000000000000000000000000000000000000000000000000044778d2ab4d11f67aa53122ba8f79d09ef5e325f5131dc0308a16e17000000000000000000000000000000000000000000000000000000000000000000000000d60e2a17304738184847492e92d0826e70048e729a264b6c7073ad110000000000000000000000000000000000000000000000000000000000000000000000007bcccf121326424b14207535c6fa891839c07704000000000000000000000000b5096d08f7b57f7d8e03d4559dfe044458ae5638000000000000000000000000304bcb1d2f343642889dee6b9689db72d7eb332d00000000000000000000000004ceb74b45581e7eee747f779714854563dee944000000000000000000000000cc718b60e27e086d29d985749e5a792a59d0861c0000000000000000000000009aea6a4690878e3c34089b4740b6ad38f359aa69000000000000000000000000980d28261228182f8339940719e5d474b11e4956000000000000000000000000901532617221533d5102232562f91a590f070857000000000000000000000000acb0aa4b7eba61736612ae63d94bfc5234367b240cf81f256935aa67d30b0868bada6e7b26811c4caf785d01e95d7423eb789b68fa696f6f971d946827370e7e19f2da6eb297e67737fa8c510808a05d440462339a921375f3f520690000000000000000000000000000000000000000000000000000000000000000000000003fe8b831456e7267c3408935c30ccc17538ed32ad2a2374d5e4162720000000000000000000000000000000000000000000000000000000000000000000000005fc358365286841b8af60b04c8f3c925dbf343586bce8e7b79866a7a000000000000000000000000000000000000000000000000000000000000000000000000819fc638cf77812a673b9161da587f1eb2a58c420000000000000000000000002cbf8b4db3c9ee0af9f35e10706fd57a42bc820c0000000000000000000000003ed8247cc2bcc73273566d6046e3381eda85745800000000000000000000000034f88d735e3123148692f9038ac4af3913106d3300000000000000000000000036980a37d4c0f81b33a396499be84f772cd24323000000000000000000000000550ccb51998b1a7df07d5e176d32e4439e7c0411000000000000000000000000e649ae2ef1e0e81c6aacf27e5b62464ba1d6d04a00000000000000000000000034553078d7676d22e6e5983560c906346cc0353c0000000000000000000000009c911c034da6ef0afa8da10695d4e14cd4225b460f8ecb2d45ac966205b25d79aefe9121d9dea12291afc9384ae4710da8e90a570c08b25ff19ead45f7a1185a7ecb0737096eeb75cda47e16182fca39064b4933f866aa4c0fe1d9140000000000000000000000000000000000000000000000000000000000000000000000009d45d603a578281392462309a2b74b3b2d4b2a48c5e109054d9efc100000000000000000000000000000000000000000000000000000000000000000000000001665ac3b977886763c79892d3564ca0299f7c566eaa9bb10b65204200000000000000000000000000000000000000000000000000000000000000000000000007d51e13c45c242163ca65e3a60cd2e2e9f911b20000000000000000000000000b8a2da21bc1e0645fabd2e3f3541bd4c886fb975000000000000000000000000fd448455b6771b1b6092a33128ed800bcb279e0800000000000000000000000031677840fc79dc64cf789e1a5c17373c730b743f000000000000000000000000ed00b10b55ff502162e15f5cda31c16f99aef90b0000000000000000000000004b47f24bd6cead5d5cbf8b325e51f33e83b4d93a00000000000000000000000031552d24e9fa5c2ce6c7f94c4b4bbf1cb83a6725000000000000000000000000c3bf9b305ef411753b8d9e2001639f213c70735100000000000000000000000066aa7d695150c02dd5942e1da982544cb8d77a4ecb85d873be72cb0a953a3e6dca9ef004008c2430b55f93096b024321b4720b317ae6f9375cf20e0f18102b315e438304eb13ea79c4db334c0e31c410fa9fe6062f671f2be2977d57000000000000000000000000000000000000000000000000000000000000000000000000e893c60f9da27e466af6f75b00ba1d0724dd8f1c1d75dc49a5d6567d000000000000000000000000000000000000000000000000000000000000000000000000369d65768588ce166943803ddf990d611dde5860e259bd5a4fd0a4140000000000000000000000000000000000000000000000000000000000000000000000005042801c1428be7c405da27d90226e4c16835308000000000000000000000000f80398715755aa2633b32c36dda57d7210053649000000000000000000000000be897a700ea75e34d9c23a49a0dd94064f236c7b000000000000000000000000a2e8f106c875726087f6ea1da91f98665f03bb2e000000000000000000000000ba51fd52802d883334b134551fa44818299f8e5a000000000000000000000000e632480cc7b9bc3aec46ff16b5d241561aee0b40000000000000000000000000c3b98e22ced5215a0d62ea26e1d7954507c31f06000000000000000000000000bcf0842627738970fc153f5b4add6b5a3319aa340000000000000000000000001cf3bb0f21011b640769a25c35ae842f82a81d3f3cd4ac4a516db831f811154e6ba9a3167ea8d514167fb17072bfa708af2d68219579247defb05515dcf88535717396584e474b517cc3e47d3f882600119b4d61bce0587727a8bf5c0000000000000000000000000000000000000000000000000000000000000000000000006baf755b77798e53a8113c290aa1a01d5550de6bf11ba54246b5c31900000000000000000000000000000000000000000000000000000000000000000000000093d9c85258d07d735d85460c2c70cf3736f16a65e33b693f50775c48000000000000000000000000000000000000000000000000000000000000000000000000bb5197774add2127759c2d4388bd64416908743f0000000000000000000000009fdf9106cc139a7a02d50b707cd8825c22f7f94d00000000000000000000000060292216fb73660cdcc48864abcb1d241bdb244b000000000000000000000000e59122436b7bdc51b9650d75d2cf1d0747d23d4a0000000000000000000000004a08fd6bfe38db22a3cc5c4e9916ec095d8eba22000000000000000000000000d0046752ef87f82f2a46984bb341dd1aa230ed1e0000000000000000000000000567e770b2b1fd4ca2a5a17a84100752f54d532c000000000000000000000000f234ca3a4f9a654c914e374ac1ed6d1f88fbf33f000000000000000000000000e9c6e07ec0da3140d5e77a11ebc9134535739e04fb421e3595ba3d5c122e0f355f30a866e1e2e57ef770d3734fba855fcbe0f3343ddfe753c0ba4174f865b410e13114780dd8b04bdebb622d5ebc510c312aae59c46d242c0b66083e0000000000000000000000000000000000000000000000000000000000000000000000009385447dddadb4347aa0f24cfffb2064330900760f7f1203ab2eef52000000000000000000000000000000000000000000000000000000000000000000000000988b3324199d3350dbd6194e93f52c763fd66a38c181a33cdbf947060000000000000000000000000000000000000000000000000000000000000000000000007118d5371374d0541a1406340e2acc2044e97e6900000000000000000000000003b5d3143e09563c2713a751f57c307a8fbf4d2e00000000000000000000000035f5bc7c051f911abcca46559ac1ce61e876d15c00000000000000000000000087595c4ff36cdf5cf7c55462e428383c15704a4c0000000000000000000000007160563fb098a508c870322256d91914b1641d22000000000000000000000000c743192d151a354f42168d52ec24fb205d7e7848000000000000000000000000576d1b1384e57d3f123fbd688c38f2071d146e240000000000000000000000004b1bd806a433615d55b7594d413860358b5b7a0e0000000000000000000000003a08321a19c3f41d5b2f3949a1e8071eccca5073e6ab95128ddd394d83b094437d373017da38575bf40cba0c6bad3a31420d396f84d26761bdf9971ceff0826ec5d18e1260968d3029d1306926bbad33921ba3693b9aa91296015e42000000000000000000000000000000000000000000000000000000000000000000000000b12d49248289d92bd609cf519983d51f23299358e2be121eb90a5d2d0000000000000000000000000000000000000000000000000000000000000000000000004666cc56213ef618b97146753e1b7239823fd01a96d13703a2918c6a0000000000000000000000000000000000000000000000000000000000000000000000005070c14c2519b5547a5723666b3f270328b6f72e000000000000000000000000119eb1709c1f3c338f6068364d03f82b94e82464000000000000000000000000b0605c43469fda57cf588e60661593005906de3e0000000000000000000000004ddbb14888a1b50d4ad0c91ad692a26c0e989f4d0000000000000000000000006455a139ec460410ec4e583ddd44cd51be4c215400000000000000000000000071a9a928ce28e21e5c3189673173d10705ae661b0000000000000000000000008993a66aec9b99652938463d6dddb75298ec5945000000000000000000000000abe5b17df85935381d3b89372474563fa867680800000000000000000000000080d842298c70325cda174e5b1f29d55385657a6d86a364556a5d5f18cf486d76cd8efe78763447668f704a09cec4470629cd3a539f7e3c455da97a589890fa76df99a51cd689cf04fdfbdd0eb877732268cfe433ea54dd24023fb2640000000000000000000000000000000000000000000000000000000000000000000000007a52a4128dba376081fdc429df653e49669d210258b59d490f07ef07000000000000000000000000000000000000000000000000000000000000000000000000b0b0e961fac4db1fdd4a0e0619295a6751c7c268d1882900459559130000000000000000000000000000000000000000000000000000000000000000000000005504b77d630ae471c581e211c19a882f1fce223d000000000000000000000000b713ad567294b352a6046846641ac5733d3c4224000000000000000000000000f1c1cf6d1d304d203b3da44d9b41b337505e5719000000000000000000000000f882f25b5338fa521402e3215d4e7341356c0b2000000000000000000000000004dd4a2fbc950a3385a48648c2b06b332c90f33d00000000000000000000000080aa650676cd32668533ca08871d1536b87f011b00000000000000000000000087a7f23d4c431d01174f163a6917e90687a1a2440000000000000000000000001fa531539ec4b454fb13587d89dbd936143aa7250000000000000000000000006cdabf64e7d4b635253693776acc2046dbd2644c4aac334908072f47c4d7af677cdf3c503fa3c70be87239422282be763173be6c8132512e2a4add101986a10726c7d107bdf8eb5beaa6226f623d3f1e20e5f976d811223c4b6b824c00000000000000000000000000000000000000000000000000000000000000000000000040158c304ee31a1bdf6ada398ff7006e51190a2270162e3927113c44000000000000000000000000000000000000000000000000000000000000000000000000552ee4667776e70c9f16d337319c093260d3a62080be365374754f660000000000000000000000000000000000000000000000000000000000000000000000004594864ae7b41917403c0b371c4b6f477fd89d1200000000000000000000000037d4f10768428e3282d6ad758082e37cabf12b020000000000000000000000009e48b04fba0f8074b94de70f052e4829de98e443000000000000000000000000ddff97614dce12111771357a4e7bcd4da7082719000000000000000000000000ddcc324687615b4c319df63d6adf8e13a1b15d22000000000000000000000000bb68ed6c08356c5127a091474ce1e847e85f9908000000000000000000000000105d65445ebfba0a363d8717e0154d2237f0111b000000000000000000000000a101a957b84e993e28aa54754e12ad66d52acc4b0000000000000000000000002101c042863cc23669558c6f02591e0e8c268f54f602c45115f83e4b03eb531c1d98a3201b2fb4068536fb10ea36a362cfef14362e6c166c88320e391d469d4107dceb339ab76340280ef520bf46265e029a25299eb17630391cb571000000000000000000000000000000000000000000000000000000000000000000000000c42007277e07fb7ca6887e16c8d825079d3f226218a10004e925d4260000000000000000000000000000000000000000000000000000000000000000000000003bc04668c181cf02b305bd783f3e384f63bb4a7be27fb47bf953f7130000000000000000000000000000000000000000000000000000000000000000000000004c98d43eede1c254454ae42b3a8c74321fbad93d0000000000000000000000000b20207860e7074def48e63e20ee514d3416d131000000000000000000000000b1bf8223205a250f3252e317f2bbcd20a173bd7a000000000000000000000000ed7fa969e80c806c4984207d0676e809e5586a4700000000000000000000000092ec1368e548c87961ea3b3d86c9365614faca45000000000000000000000000ca6ada5b07ee9b60d7a1966d7eb35f19e7f188320000000000000000000000008006923781ef31751b07f25a5b4176462c126911000000000000000000000000e0ed435da89fa619a599ae045164722728d2b27500000000000000000000000069f9dc3fe9858d09d4e42e1a3452bd2d5c1c4d78a502c05fc9a1bb012d8f155c7c022f532176ce5f6c88d522a41790275f6bf53d199353522cd2da06cd81697dab91dd72c51b2e5aa055e918a030f758823d0676fce25e36199c6c010000000000000000000000000000000000000000000000000000000000000000000000007629af667a332059f66ad0736ef2c656f0e4db272610744d13976e3d000000000000000000000000000000000000000000000000000000000000000000000000b06f5652a57b9f20f60bab3a8a3a5825237276118c98f7780f8a7c5f0000000000000000000000000000000000000000000000000000000000000000000000009c91b0411d53f536a666ae373dd6960371fb4f630000000000000000000000001e6a06343e952541d9d0ab3bdb1e2a4e71ee98460000000000000000000000000c535828703e4438b653480e1b568861feaeeb490000000000000000000000000e452d13bf613f02b571dd174aa50302569aa301000000000000000000000000fabb9a60e8e4654353858165901e776c3046bf6d000000000000000000000000a87f434c7206fe4e0a02504ea8fe27366d3098240000000000000000000000007091d15430652740d65be45c8eba49540895366d0000000000000000000000007b0f8d59dc0398363537de7e8457c7608bf6025f00000000000000000000000049f3cc0e4af8b83ba1d34525a9a17c077e3d882501e5981954ebaa136abcb06b74c2086eeb08f36b46c0275f5add6a08e846fe208fa5d5383f22952ac2bfa836d4c0842915f9b5301e9db5414ebae658af13f275cdcc4a5a1065d26c0000000000000000000000000000000000000000000000000000000000000000000000000cc9565b779ac02c8575b605692b422da731fd2bd22b672d84af3e2a65cda7763d8f1e7ab592d8516c9f623d255d96171c2b0e423daea74adc978e57155e580c1bed6d66613e133458e1796c932d904d1a36f938e7d05068c40efc1732734c62a0f7d66a846793081bd15c1980564a34106ed47e4abc654a1ac9182cd9ff3c30c9a1a73728805e0c771c194dc269690950a4b30cedf14e2d6971ef7491ba623b548911388eef884a08f1b166ad7caf3c97a2506c0d6de4270146b31e4ae00a1c74a7d43631ddb3224459c63cd2992b11f046e840828e281435d64408c8e0be72c7dabc30383113111efe780d94c5d1168799fb2d3549f71538adf66fce1adf4b1a350875aad1295259b9ec455cbbe06feb79a613ed065d0a16779721979b60234c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3572f790b78bf152a22d777b43c42e7836b6d0dd74bdff7363763410074f587876235f5667eacc0f50287e9eb3b64d63d59eae8e11946d6f17ad487862036e767559d604235b5a5ee0be1cf9e6c8215175598be2529ee78a431d1e15f5fa3809151ed124d6e615ce4046142dd11589ea81bee8531714cb81a492a51f16b9b58fa5a3442292efe0cc24a0cb84a10480c3405606b3967dec6a74f54c82f02c8e9e903d9d8995a4b136e2f1f68681194497702452d5878f3d701776392bd6a8bc2307d4a955e41b9be07645063ac57ef9dcb09bc3326237d402b4e87a0611532249627235b07116cbd433dfbdba55444a1634059f2567a2a8c2908dfa76222c988d14dd775f2639515321aba9b7e36e1710e4e0c0fa0167afdaa4d0a470a5a277e9059fd1bbb115fef2e799b81f22ba03d86222a34405eaaf683739d056807b9960b60374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3572e05572ba1323051c867d334cc876844fdae512e51c9a117a8bada1a94f8e50731a7021c8407de1ee80d0d2b0c1e5b24e8d87d28479677683490fc3d2aef832025614b6edd690410bc7cc24bb4a4d010f33db83d40703472fa95f075724a6379ee232e6bf5befe6e54f0c13775d5090b4bd4b33526f8c30eff976064b5a8c71e5add5c3e55037241d2e13a25b94ea052a4624b0aa1f7a37c9361dd6967d1cc7ea0c9800972c6a83fd45fd4238d5ed312f926b407e16eae119e386a5fc5790450a624c94f4b889a26a06fe67ef64931177d110522cf6d7a2aa0efcd5061f0d7278d8eeb29dddca837750d69095a778c1ddcb61b73a1d72d64ac0e73596a8b6c268505652ce65100363c3b5c5097e4c942ff937173f06011125a82db07f6603f7e386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564a0d21543f303d15b3c025556770fa5104f90351faa41b3380e5413670359f60d18282030f56c5037e097e50a2c198157c25c61369265fd7e56046d177cfdbe4e911ca02be5ea82494e2a7e5b80d6503c35019377165754133c575459b853a41fac268f759cacbd57c99a7c00e06e4d060bbdb95205679b31ea9cb4413c74c92df4278811a85c827ef869341df4ff7421be57d72e2ae2ae040c8d470f05a25d3ec388d21f79a39845ac1bc01840f6662ec0419e34bc2e394603779a21ed724c54386dbb4d46ff925b2a7ee36eb4e4f95d52c9d3226a43ca6b02dc08460de4287243a8c679224292161ce7fb5b782f150993ad2121ac67ad45c6c118276450fe6ef77dd474e65b2c3d45c2210e834c26682ee0a60806609e01609d1c68df383a412e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564f6dfdb6cfcd2af094ec9a819af0b8442d2d6a003756ee727b55c184d8ff76d1c8199a278e37e8a5b83e4735201a1ec5da321f956d6636e7eb680470dc49efc72247f03611d1fd5061175f61c86a3691929215926d58efd03e090e626390f5d107e2dbc0f20b8c46e14f9816a626e645995bae64d4226741108ebd7195bba893b0e352e7cd7d50d161c0fe767831fc471a8ea4d61cb455c09b290047184955334f869c41a954d3f67a3869e387c77377d223e554ed9188e7de9041a67723f4f1b5223e5511a2c8077248f3d3add7aa45b52e89e3b3a647a42f8fdca465f2d4650de59db1688ed7f58665e937d29964e5e475f81376d4f1c33400b97388183b6353cc4c07477e3803f1dbe0a740b1ba053c1d9a377345c656c1fe08f14f2e9a81bc7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35763228a0213a18120e5a3a40f5ce4d73e3a0a6b5849e74b11ac79f44c2387f60befe08c2e77a96114150ea1139a98925429ceb860fe9ec938ef11b4560475d9365064ea1aa54cd27aaa5e3958562a1157b804ab04368fc107db951a3cb00926603098b079245d5a4fabdec242b8f61178bfa62944c0f4bd02b3fffb11bae01f16e4d47a0d90d41308c8d89030d064722042d3b245b47a844f3017962bf28ec367408c944d2f7cbd7e7ca7471d3a41945f3a25ab6b0d20cf5c3b17fd47c5c3c80d4b6b8d1369d8bf2ab44b5a6bb6ea5b6528be110208e09150f2a4440070ea4d14f06f371e8a169e3015430f42ebc9b052be3f4726f22a0e47fe6f8f460f20a467ea75c34b4c11e356788fd836f65ffe75a7cb602fb17c07072511a965ad84562d452acf236585013f9e99821ac0f51a20ca17aa70ad62204ca78c4b66074f0d15e5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357236f1a250c6e3e5d5fbbcd3af920bf2e8f33f4485641d97bf9ca4b24e6859e3901b92d65b52ffb490ec26155252bba28280bf921692fc1586ac4ad6fa2c34d377b1ded1e96cd601d045543325169c07101d4f53823cab567520d172cfd2b7e2d0bf91428b5baf94afea4692ad9e7447b8cdfd95babf19d0387bfc9098d41bd12f04d84278b224c1069a68548212cd373655527555aebd061970f60496e6ddd6b4bcfc63b870b1b2f8431a91e4610dc70336a3e5976dd0243478894643f49e02d40d57906512e61082585973889e08367c32cdd68e7f2d000aae16e474d328329767adc57ad4c9a2bce39d57023a11d7dfade216fe5fca528f9573b312c1d0320cfa4f358c32d103eb855981b8c10443fe1aa6234d888af3dc5c61a04c3de5803cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35765f6180d1c86055eaf1f9042ea1cad615674621c9b838f695c25735ea12b46013a35da4f05d6c831b4166c18e692f40e20022d126cde331ed11df95dfb7d5d149882e93971c24e71c1d4dd20bda79c50e168bc703247656f8f1c1039232589492e1e2465ca13b348ffcf7358006fb438524180594a5fe639f49ccd284d6fe828cd5efc2ea0a2ba3160a74a0234954637f3fd6d7cbb0db2167286134fe75982745ae85f4a2ff73c2107f5dd3b799e0e61c4a6cf5c31b74b2d3bc5167ae758075df8cc7c2da7e13a47261d033b26634a0ec11aaa6162b99d71358e521ea76360658d8eeb29dddca837750d69095a778c1ddcb61b73a1d72d64ac0e73596a8b6c268505652ce65100363c3b5c5097e4c942ff937173f06011125a82db07f6603f7e386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564ca08881bedc7a47d6f882743988e8e32efeb96418941f925ae2ff447dc66be617c6dc723f50c4006765aa439fd61d325a1250a255c0b09586e15706d89c588560bb62c3d8e4d4e13841c277eac125e6134b8910f686beb3f7944a7434ae501200adbf65b4bc13039fe80a86150fa0306df238d2fbf7bec686f2dc26fcb35c30edb8ebf156c8c1f0e269af22d579bbf45584f03516d562e65d55d511231277b10c523332d8773411e887d5464e40f5a3c902daf5b44f05736bb57d80090aee9711a62ca18324ac34c61aa3e1996e402469855f4592dce961fab084c6121618566ec67c13cf86c2b6ff194654c93a06f59f8e1c04523eb1a084799e3221ac834784adfe06834c0fc78c36e5526f3fbcc254715544874b78a2a71aacd094718082ac69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357bb8b7f39424da4211db68065c95ae6354ff1c84cb439cf685c01ad665bd6793678d4c53f58e3be07aee8ea044d33c62566ef2c7db12b6566b1ecbe658ab07533c40776771ae89a329cfaa148286026144af889468f5c0c5fb05d636e81d07d0944e44b739e49a85809ec97653207677797e2a0125e49a20ce6002c169f92372527cdab4fff92397c0b0af0264217382cf1d4017604882a5e90d4aa376290e707eedada020bfca61454da485ea919f03825a53f500bb44b75f40cb31d495f3f394c713978ce692a15ae98832a60dae66ae379ce68dabdfc1b3444c85970633770abd68e0646527b02eb33e700bbaf03309aea6127a34b6358a45baa7a294b8c7027415e435a2fc7365019553b214e0d285b2083062ba60d35536d51034d13af1dc3bd7931145b6730d1dd060b0acb520ddd8e3e24b64f3379269eae25101b646f2d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564e31583295398f86190456d023c4a8021c845c076bfbd722a524ead4751efdf5826ec1e484877fb6031db1f72a92552442ae62f4a331408360af7ef1540708075131a0c2bf12f3908e885fc056b83fd049ec10f0d920ec34eb542a452c6234204a5326b045d132d186ec21e64ea725e18d004ac18218cc32536b2de573306b46aca18821591e0a51e9b17b51a8dc26014b08d563582c0a469fe835a4d9b66767a77e34e1c59f6396e33b80f0f0f2af80e7c5d9408313fde08bea632482d64f23993d5c34d9096c925aeff063deaecac2ffd48df60b3a2033eef7e693997b684564f823d42fcdadf7e11e3b924cb56ad72d0475446db9df8189dbfd9651e55656d8475c0783642ad5419a1c36f2225b000908c8209af2c9b115d588e3d168035074c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357a3a1ea754ddddf17baa33f3c7692be524908e249d84dbe1a2e5dd315fee8816eb12841172476e62fbc45a97b5cc6b43138171e48b43bb548545f8e2084de6047d3314c1719098e284c8037612ad9a96ec5d06a61bc7ca904754e7238b14660453dac9622b5efee59bf22e051114ebd4d9256e931d0d1715d20f23e3f01bd191bbc01815aebd75338d72e4e0463215c505a33bc5e6b50bb3507e47106569f5d46be008f6298ee151925633a225481885309d23830d8c3737e4bf52c42f5676973e23b4f6031efec6269aa224c6013654fdd74133daa02471adf267211c96dfb42ad81527ee1b2287ceae9c60e8d98677d89fddd47916a77682cac2e6e05261a1d3cc4c07477e3803f1dbe0a740b1ba053c1d9a377345c656c1fe08f14f2e9a81bc7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357c349c113b827da727507a804189034694dde1b54a486214404945f63e51ea146771b6a7209cbf677b8f7c9726e31a3096dfcd540f042146067455c7a1ad07f49f79f044ee4174f1bed895d5020f5ed54af29f24183bcbb464a28d32e63287a7d72886c7b76036b6ee038606c2b162a78944f2d6a32b7647592b24059dd53724b98eddd37343bd65a9742e32722a3337aab47713909d1996b3a8890129bc0945dbf527412b324c402cdfd801513d715304c157f0fe4173b4f27952b52bb3f872600604c74a2c58152e9652f7dda06d07691ab7239036d1137b5d9a127e895150ba294fe49e9fa6d2a42013b744fd26a3d14e149348604c14898587374a7ad4d14fd71ef24c0a6500b892e1f69048f1327abe903296c159966b38ae45deff8be67bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956452eb096a9eb15145314d965d3ba00212aa13b60d9a5a5c260ca18d1a4f180139887f562d31f1c83ac551e36ad69a4c2c8396f3063f5cc16c9b41be07831f42666acaf1408504a44405c6f36f1b12f77bf87a343fd21f7434671add2f76a69121e73998299966a45d516d0c6638717567b689722c7e876142c86e0621d735ce160e4e36763a09f65d3adcbb777d56a6520500611fc3f3d966264b7d31edf32956def996443de0824eba6835017da3417d42830756e32c205887876e33e4edce1578fca75824bb462a8c7bcf1acdf4fa3b1b2d0a186251841f5357630d95b7ce75263f4401a7bbb7523449787b1dd12a244bf2d361f9ec2578a4b1775459ec07674f37635b04a5b436f9d3dc156d80074365157b70c2e5961330b21e199e5e4068343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357b4064f3797ccd51993ce935bc432da3cf1cd097c926dac15c947ac557c62b552554af73385197242daf94466b3a1157053b6be0750af3b4e8bcdb645c2ff4352f7bfec4bf32f353016336d31a0546c4a63c5ff12366e283e6db6f103a82e4d082a5f34063b92510965e0f232163da95f836bf81d82b11c2f241f5d6a8bb0f0559c3fa6168275d81102da6b73e847e34e3ee1c407ee89a3641bec7a13900eef274619b27ec7b1ae474e6c7d4bc205837bd1426c0dc037db7b2d056b00649ce432c03cad1802d2917ae0f91424c8c7513a787122018827330cdf2ec66cd3e91a3d3608e86a71752521317e1a42b4dd3918906aee763814bc739f676e4fe4113d069f5f0e4cc792265111c5d152eee0ad6972fc8c2df5dc84568afb7831bffc372c2b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357edf6e84217cc71447a3fff117c61f14dd9e2cc4e636a281b717fc841789d8e6e9c4eee1fec46df64d5c8a71c0ce1ea05f197e71527b9b71b3bce9e76a3f3d64d5c3ae63ad60e9a7ea04b4a24ed62a1223629742a7b154e3a3557ac498afac83bbb381f7925a8d04992bc386ec91043667f26b9739ae4ef5333d5e8356f2e3e299db0ff5fe7ad8453f795cb0c35885b762c3a7c74366941229e61d547e891c8059f60620faf19867603c41f355966a07093d90e3ce506b52000f8fe7c2b310b082467656a5cf838649c8c261ee6f77c59e74eec1abbc81b0d6b574239e00ea81ec482445655edd56f40a95609d2c1d64a0ed5d140ea177377c3e55206399e12339654745457d56c6c42465e124ab92121606bf42560f8404f8096c8119b2b4b48343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357535c1f4000ac4667a79142227d8b415d076da31527b50e7b9830b825ba04fc00bd1ed97cb72fc279fd33cd259e510e07b860b45a5724df18b3e4ad47be59f33484829275e4ecc63fde540456a2ede5373cfb9f550149fc1c34b81734e191b978fb976a6332b8a13d291f3024623b0104f6152966ac1db71de4126d7dcefe86609b74a1664cfabc2ff391df1c4434ab1ade0648732318e23cd469d96c5f1e6a4cd7987579805b4647b02a641ba1e8147a0e996b6ff947d92588584727b9ee904aabc1100f76e25808eb47835e0373e53455f71b5de6e40e1d6f10e25b447884390c845875735312300bc43109963348057d70e9493050816a0e40836c45eb872625e26266e8d42f2f4e3b97094036b24d676dbf64b5f1d1404d934c481d122b762b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357346b850f8f117a074edffb3cc3e75f16b086d803ce4a177bbe043c1a25089f5444152724a34786277c688e31c3df027989ceb64de3b4f217501cf7130158387be0d4202eb557d332186c50563ce3d80befe9d315dbbf4856bba19852f5b9b015c3c7095e542b142cdc718b42f851b8328f919a4063e5be533bf5e464429e964c8997e03b5c169a7b4db13a03d2b1eb2cb1777c005bb58e2a0e1ef8350a0e452b53599f24d9a4a267ff149f2467ff3d7e802197557b60eb278423157b041c8376e23b4f6031efec6269aa224c6013654fdd74133daa02471adf267211c96dfb42ad81527ee1b2287ceae9c60e8d98677d89fddd47916a77682cac2e6e05261a1d3cc4c07477e3803f1dbe0a740b1ba053c1d9a377345c656c1fe08f14f2e9a81bc7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357c3c4141b6b08ad31d4ee67014f69882de5ba827674af381e4bacf770d7d2390ab6ae192018662b01bf1f721043f81f500401c84dd3505e7dd3bc3b5afe833258b6f73b259e34155ab5638b568701f07ad1e37f061cdb1771ab44b83bac96530bd5f2ac5c32ddb4793fea923b8ec6c717af05766ae7fde25011bea52a21f5044d882db81f97fa636c05fc3b41e6b37f3905a6b7265dd9051b42faf15b3a7e2a06d7888b02f0e62c470e83922ccf28854c5abbb80180b50e12f18ca96acf14e1566c210c376add6c1820289f1bbbb153532b4f751d1243352838987b0581819b10ff4ff120689cbc538828512ebccc4744b613bc234eece43b16344c134af28d020b06ff0ad5a9a872cf09671d4386da25a8662333238ed922417025041b88401dc3bd7931145b6730d1dd060b0acb520ddd8e3e24b64f3379269eae25101b646f2d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564a96058333e764d2026adcd430626c467f2f5014040e88b6c85364b1ab6b6d518939cd3373a2bee0edbb2fd3cf13d102b1827417476770f4b9cb3f607213bc6493a114e1f6297472133a0743ddb42623eb98b031b8b4fb84bc996af6cd37c4f11e38349731ed9f41b08f33a39607d7a2e282b28767d995e3207778636e72bbe792ab2a948df312e2216ca9359bc7bda165276bb1dc66d465b2a57be30d3b6626d7448846b76dc00039e2ef70e7beafd300519e22b9605ed798ca13579b0ccd41c4a955e41b9be07645063ac57ef9dcb09bc3326237d402b4e87a0611532249627235b07116cbd433dfbdba55444a1634059f2567a2a8c2908dfa76222c988d14dd775f2639515321aba9b7e36e1710e4e0c0fa0167afdaa4d0a470a5a277e9059fd1bbb115fef2e799b81f22ba03d86222a34405eaaf683739d056807b9960b60374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3574c130d385565683202cd846e75e4792783ad07266b992d38e7d808183d1761199644960b7e354910408c513b598c106a8727f93fac0dad071b065b7b7a7bb50f536a8b28a741932ce3fc5e4892599b69f5e441352845b756d742386e7ee34e3a3746f542a770696b9b919f6707d05e0829d6f0734cdc5475781aff4257561815e4054756eeea103c82dbb8176d7f640522280771508cd639841ec96c687c1b2d4619b27ec7b1ae474e6c7d4bc205837bd1426c0dc037db7b2d056b00649ce432c03cad1802d2917ae0f91424c8c7513a787122018827330cdf2ec66cd3e91a3d3608e86a71752521317e1a42b4dd3918906aee763814bc739f676e4fe4113d069f5f0e4cc792265111c5d152eee0ad6972fc8c2df5dc84568afb7831bffc372c2b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35764f1cc632958853e258487297836d8221ab81d76cf6aa6064ab1a92bfea1871e9ad4683553abb23d6d2d0e33a607bf72a047bb5bacf1c04feec9a6628098eb42942eb028245ccf28d7704270e38c8601ccaed84abc7ae1658bac3621e2d80d5e047b4a41f8201b285b66ff5510ecf76f60cce0439c2dc353ca0ccc0827f8b8753bcc0367745f9b29ad803c12775ce50f80dede3fc5723d30b2dd48694d9df54b4ec76611763d972476f528298c2e2521f5a76355a300e013233a4a028cb4cb251a30b41dc5aeab4216225f4fc2dfb438ab91e043a4335a139866ab0c3cd16b080a7d090b4535af5ed7056e0851b9c879dd0bb341706468763823670340b0f8548505652ce65100363c3b5c5097e4c942ff937173f06011125a82db07f6603f7e386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995641439776c2aa22f1440f0b40eb8d15e08e80f492d84c6024d70ce745307dcba637bc83847d9c183348c50804f0936ca5a592435141e29c4314efea4217978c93217f8994b9a2f25244ed7f57862d06f7030e9c233aca8f839e0a49c5d9a7dea4787fc051d92249c316e542f551b98ff37514861435e7dd3031246af686ff67c09efa481259d5fac3460e57c4dcc16a253382e2759dd113847f907fe35d3c0bb4b14ecef54047e9151861ccd4a03335a150049060b88c7fc0c3966a811dc407634bccce8160a12190a1d9e5b54b0de2127062c6d27806c2c1c66bc4812839d5649c5bed61bf9178411b95df3292d882d75874b377339bb983c2238b2273265513373752d6889912e3111d0607d5d86d447fc2cdc3a7cc3e80c3684d830ddeb14109d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564075d4a337617d36a12ad441774785f578efffc2b27ee2d6ebcbd954b12db572ceb2b2e79f0a3c97059d6a6320811b714e39ca26b09b0d02b4dd9664e20d3d54d75ad37124bd838577e37136904b189047f35562be24d1a196feb5b3cd2efa3395f95770a3948136987ad551a3c8ff42d8cc0ee5518579d043aef037a9a2ec171fce8e40e26613451c8c68f084d783905adc57a7a4bd9e256d803de3012e9fe48a4cbe6103f56484b44dcaf45ea65a965f3d4371429b7c64b52708b26ec854f4dc50cde267b1c81339b542a360ebfec639f663b522170c022c7e529107da3d174af06704015e5cb5edf56ee6cd226c45d12dce04bb3410057c632174e195f073e36c50b0590afaa39a7f6205049c8986c20504b2f07603146cb588e264f09ac19c7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357a89afa39b6116e5ad767ff5f6fa95241eae25a214d58fc334a08476ac232444e2fbaa03f257d20264459ce3a8569f91fade50b1c502c394f9f9a8326c9108b79645a0d7c13261a40ee32084a042a3b02a303c972e93a217e4277a359641b5d0b502d604479f7346223c7fa65f1bb8568f6c7981297694b7204e04704f3f9a162f2fdd15574ca9251502ef21b784a67286ce6a00604a3f72436451f60630f7332544c3535d7efb34c554fa04f31f0596e67800c4665985561ed93c85f51984d104785fe6337049d2dae1ea2322d2c7e5fb3c3850139c3916c1ec5927c6ce7b933be2ffc254e1c5d09e5b5f525e0feb86781f6505d2b0e270b9421221c90cba26a83241d1870631e19133b1b670f76724ead8a021ffeb5a956aa8fc25e0db76c350198ba3a66b0a6131254b570cbbc44359f47067ebc15520c5d8a3a791b5f7d772d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564377ca346e75899139b172e12d86e1547dfc0b21172e9cf1ab421250006822328f694c41e96421149a9ed0715fd5bcf0223340b20360cce779eef6d07c64ec22961d8ef7c5ad15735b4aaa162d0af901a4b1cc90ac61c637b6291240e7ec42b728b58c62fd95a430b2a2f1b1905d31735660574107413ad138a390b4cea6a1f08ca853731065a8a3f5842f7303001ad6ca17e821cb75cbe3955d60d15f737d7048ec51800da013853f4355720f9c1810de32e281d56e8411ac4eb586603f2016cf265630c361f137196cb276e503e6c1bc725a16381bd684168a08a5bea27087593ff5b4bf8f1e12f3e4faa775c19c65ba33d4417d2d0a31d913ecd477349580bf0fc851582805202d48d6834747ce0064e75c950a31942540046974e65b70a01d47568213a89630c31aced59c348466f978c9b539c0e0a6855f57f47134cfb0a75b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956467787a64e80a095d92167d6b16c3652b424f6e55fa0b34794945827e3063041ff3e90277a535651121332802bfce651b888fb3168de68e6c7cb6360c746c67706396f445b0c9f5410724167b7b1df412a3897703e09ea35718328871666061164f17161c0179f22b5d687741558df6072cd89d5de508f50c26f8467667072c6f5b6dfb560746fd6fa15bc76acbeccf5f82b013439f768022274a170d2ac32a5e7db3e45d2d16ac505b802b22755f996fbcb3a65328fb1c3761863d616c142a7c24c57232c3cbeb705aea6f3f0886804d8632de0504227827b3ccef1194f6610cf70ab1548164547a57594134a88ded6455ca68630df0215d7d28127057a1ef3d0ac9d80ce709de6b9154a317d8d0b61fca5d372aea3a931261bf551d1743ea6dd47568213a89630c31aced59c348466f978c9b539c0e0a6855f57f47134cfb0a75b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564e7682d7eb0faf60a0a680123c3fbbe08c19ff776b0bcaa538559a06fc2af9e5f591b8e52b63bda1f95d21f7d249b800d2a714e4d5e087130b80e5d04781a8048ea187e1380bd05174c677c6ef5387e06ddd1246e0382383e0ae25d2f3d6e375c8aa0a17816e7c652f4a5d47d7a6e2a1a9167d44e06e6712ff3f47f6eb883824c39894b3fd785bb29db47fb50249ec77d6fb85336ca588c5fa2cd8a2a55e4c04c0e0dfa117689270fedd31503d183d306ff6e0652ec3e933a9fecde756e1a9a24c225ee79647ed75d97faa4681b29007db12bea381506b719a7da5152944c9f0c1b79192816b9765ef6004427c77d8e3c9e42460d89aa993c7788ea6dce3c0e4c2ccc5c11f01e933c764c771cec11c661baae67618567b110a00d310081d0db130198ba3a66b0a6131254b570cbbc44359f47067ebc15520c5d8a3a791b5f7d772d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564f66df5340eda5d2361e88f28abfaeb0366004b0b61f3351bfab6292f4d488b647407d6117a570b0084a7c41627514c075c870c624c00b000b4d463223b33ee13217a3d3d1059664efbdb4a02be9be362b424637cf95414429457d605aa9899684746ae06e2281722ce65ca0a18de1261fa7449457ea2357e07e9e43d7b36341eef54793e4e690a6993535f262d743716d67d9405974dcf1815fcfd36bbb57430ab80af60585c6a24868b701412717041b1697724e505dc5c00f49b0b8ae2c441533d192a03b01436667de167e6f5d572a278447a4a20cb3392f16324cab92c25be2ffc254e1c5d09e5b5f525e0feb86781f6505d2b0e270b9421221c90cba26a83241d1870631e19133b1b670f76724ead8a021ffeb5a956aa8fc25e0db76c350198ba3a66b0a6131254b570cbbc44359f47067ebc15520c5d8a3a791b5f7d772d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564b4178f5cc5bc730f7d268f1b6c973a09e75ca703bbd31372c2b169112a902f6baf6468558bd24c6b8790454e3e6f2f0966156f00e9dda40790e52375a1a50327d6a9dc439dc1456b82b1a6661e2b8920c2e92e6c083d467cbee45970c887063d5efd987a92d2dd211ca5cc2501a3f6564eb281432b2f56271dde3c30493d69730e4e36763a09f65d3adcbb777d56a6520500611fc3f3d966264b7d31edf32956def996443de0824eba6835017da3417d42830756e32c205887876e33e4edce1578fca75824bb462a8c7bcf1acdf4fa3b1b2d0a186251841f5357630d95b7ce75263f4401a7bbb7523449787b1dd12a244bf2d361f9ec2578a4b1775459ec07674f37635b04a5b436f9d3dc156d80074365157b70c2e5961330b21e199e5e4068343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3577d45404f43c842691f584c13db3b2b0a942caf50f4fbb82c765f0f4028838175e2b27f7b8beda8053957bb2965c057419513805c1bdbaa08c8fa944dc1a1397420cbb644c3430471b26f8c3fd5ada4793f1e76277616007c9f0f9a172a2b6723ee1cd5257dedfe7b0cd23c320f3b293444254157b64505363800896983f59679ef08bc43fdcfde2d52abe86a27dfc9418a8ecc024cea8d38bd5565656b70f450d0227a4c41ee2c2de63b435c340e1f35c59f1d22562137396ed20c2914b7ce015dd4c363735c9516a42e4541e39b91536966397dfed5ea6b59c7712c073df35776134129be91cc3b6479a217b0879d5864a3af48becd831fdcb0c75580214d059654745457d56c6c42465e124ab92121606bf42560f8404f8096c8119b2b4b48343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3578905d22aa233b3651d96e3701ee52773470d0c2b26baab45ddc44929281e36258aff9363e345370db00e5a3af626732e7293ee6201323b309a3cf52837508008396c3d671c92d632ce635b24c0cecd2c1762e7188ce8580a748b84764e6eea6d451eb832abe9054f3438f5307548ba62e4fc4012358fe800cdeb2e622439e71b1a86b245a171cb4c32467d766e311f443d26a023b156aa2c5f911b782f2c4354f80d7522c31ba6217da3095ca39ceb3e20109d1665cae82c6edcb9311a5bed601a30b41dc5aeab4216225f4fc2dfb438ab91e043a4335a139866ab0c3cd16b080a7d090b4535af5ed7056e0851b9c879dd0bb341706468763823670340b0f8548505652ce65100363c3b5c5097e4c942ff937173f06011125a82db07f6603f7e386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564bdb6a7593aa5cb6a189ac31ac569a22b9cf0786f9a6e8c2d4cde7e4d8aa6026b7b13df71d27c894b2c426935bfdcd460d25f352001f1336ab4875c091423a84fe814c02b9fce571ee6ab57453d69e437ad921e26a442ec03fbab8600389bd0602aaefd05ee3dbf1affa1fa74a1255c4f7a12c423109af42710e440628512992da2cdb80ce9e4ea709aa46f05aa0302220758ba07ab3aef79129d637a051bd145d5a0e316c01d4f257da30f34ad5b500c2dd853665c6cbe10cc4e5825b500943e6c210c376add6c1820289f1bbbb153532b4f751d1243352838987b0581819b10ff4ff120689cbc538828512ebccc4744b613bc234eece43b16344c134af28d020b06ff0ad5a9a872cf09671d4386da25a8662333238ed922417025041b88401dc3bd7931145b6730d1dd060b0acb520ddd8e3e24b64f3379269eae25101b646f2d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564a9f5822a5b9ce92dd58ccd6b7343b82639df8c1e84267635cbb468347bd52772df265824bf38ee48725aaa76d742f34625766f03b58ee058e53ab03de687365b2078a3663fb1be7ba6b7fd5c07dda970a3f68c025130a602e01578442c5cb43629389d364d988233199c23063249580fac5ef95f40f14242157bf4532a0f48725afedf6ce155b00516eba66fca56c37c0d94ca34a6db5d13dc82e26ee1ca71064630bd4f10b66653a9d1c34ec59641655882430b143eef7eb440413eb6af3a0ae3a2605c5eb65175eda9ad7402aeca335af2d332d1e52e19e29e99275a90025d378caa414dd19518ca3f884f8cb1bc697b502c17b9297f4faa0c3c53c537475c36c50b0590afaa39a7f6205049c8986c20504b2f07603146cb588e264f09ac19c7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35789d07f229b877737fa74b1309be3c144f82bf516887af14af87a9f2a37d65f2b5c5fa57c036b65335fd31d5a859e334d4c06165cb9386f49708d350ff423d248bb59925d0d11d8029416e30760ad2c5c0e6d1c05c1d016200ba61b7c2fa52c0e9a39a52c08dfec15027ff04ab8345a77445b2642d04535536c481359a5760c15c5c2aa6520b4910ecc46d76ae6a44a00fa932a1245913c1d3d6e332af12f3f3f454c5f5466df97459d86980cbe0792661ce4f71eb32edf35a4b3f0186281de63235b17122c9c84572f949f0d217d0c78172a86302d86c1657111821005efad1975026219145970773ddb9a1d8dcf9419b2d39a0b2a48b60f0d12be49d9eac629f77dd474e65b2c3d45c2210e834c26682ee0a60806609e01609d1c68df383a412e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564cdb5b73594f77e6a9f1da82b0a64707098d79d312651594a033ac534a2751b1dfb694d03fbadf45c4e83500800290d6447e3032d4f0e8a727646b55bcaeb5754e68b9a4bfdb1522ed745064961b1673f87a59a1fe854621cf1160468fd27a67ec5a6e861e9f0fd6f506f79798056136adb7e9975b42dea32e0590e4d742ffb6f5c886a41a176651d5e74b334bdfc7b5eff67551d0e8042344939125eca6387551d5bcd7532692657ab73c20f142432635d35f55c57076344e18aef402a104c0c6f648a70ef47c47466cb6b684adb933c91a2dd4a10bdb61595e380047963c02693b3196da7aa9410f21d3c618b3a0375380fe4174e5cd20239469a1d2114550dea75c34b4c11e356788fd836f65ffe75a7cb602fb17c07072511a965ad84562d452acf236585013f9e99821ac0f51a20ca17aa70ad62204ca78c4b66074f0d15e5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3575ddae85877cfc319303e0671c30c265bed13797ac8f24e5d08a9371a6334b12df4d7fe6d40538248e14f6852581a251eb3334a59610ba0658fb10424cc0b0d69d277ba751abd7d15ba88d7275c9b216263f5c031e6c17716068a2855eb780f4a879e3267f6ce0365a057a429aecd4818fc05bb603faad37c33c17543dad09f5b90b34d134cadd46b53a3eb5f6329a115f0844d12f4ad0d5c47e0b40d17eb1c23de1f1e620db3877b177fd856e2c7c333727a7c29e985b6307ed8691cd01b0807a624c94f4b889a26a06fe67ef64931177d110522cf6d7a2aa0efcd5061f0d7278d8eeb29dddca837750d69095a778c1ddcb61b73a1d72d64ac0e73596a8b6c268505652ce65100363c3b5c5097e4c942ff937173f06011125a82db07f6603f7e386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564ccafd26cccd3861d9a6de55bfde0e222e872031cb36514717b206d6b2b3e0a517811e602ae9e2b49f8e1d00e9eddca2b5889066138d47c5fcbc4391da37287535b90fd21a4089e2657c94601a61e4032d02abd667d04612c250f57546812ea0ac54d9b3181fc4f1221888464cdccc053d2718268c8d8cb504584d93091fbda1e8ab29512691a7b1678e3e80259eb5e4788dbc97046e3d0406e1f010687fc19286c5ff5530805b92be412e6497dbfcd5f2154d567dd3844764975a842cf02aa4c0da41f77d98e972ffbc2631daf5e9b2223293f74a984e41e2d8a1e1a5948b71143a8c679224292161ce7fb5b782f150993ad2121ac67ad45c6c118276450fe6ef77dd474e65b2c3d45c2210e834c26682ee0a60806609e01609d1c68df383a412e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564f123b55646f9834c060650061f0d005056cd7f7cc6c61b3cb12ab507bef40965a64a3237c630686451092820b254cb4c74b14b1b2b971257677bd971f26d325bad7d360e8a6c93311f11c54e02e745009efdff0bfd9a1a2dd8c4ae3bc98ff92c7c983a1aa3491c6ba9a4e23bac34695778647d112059851c4b5a72747d933825abbe525a4b7f6b41fea69a5f9af65b466af21e082bc38c68cf407a15d1aad74fcc2a4e34df5b2b327fcfc94b50e4f46874f7f678bd51e7167311c64de4f38117f25fc65cb397985d57bf5f1d224e34511cb26f4dc426e808f99503782e72392de410ad3d4495081b10f117761676417cc299874e9c3eee0ec273d3444c39382cb9dc990e0649c20c8745be4dc379024f1f156355edc44f2d0bba947abd14e23c452acf236585013f9e99821ac0f51a20ca17aa70ad62204ca78c4b66074f0d15e5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357367fa732e8a69660bc53ca6c8c0dfb305ad4dd63bbdd17637a96591222370d39d0f3fa4e6e86882d8f5c6d31030a014e1ed8a010cd3a1b0647ff5b13e3d23c1834759d4880de9c4d500c4b57420cb12497cf5b5ce61dd92b871c1b4182085863dafadb0bf634c35284db9d6f27650b00d7405b569ce6a178a11c9c0e54915b06cdddea0c8094625541e02b6a86b2415d2f6d5b54e77d665cbec9a77723a9ee448868223315d852215ab03c3ff3724d6d2198106aec347037dd486a66e1d26c4f3124346903f9c96478ad8c4caffefd2b732e0d1ddab90c5078ff120b4c6bb00976134129be91cc3b6479a217b0879d5864a3af48becd831fdcb0c75580214d059654745457d56c6c42465e124ab92121606bf42560f8404f8096c8119b2b4b48343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357063e7d322c689215ccf5d376d25fa84c63b2dc04b643805bb88ec943138cde1c145b03310e19d2176a38ea0fcbb60978b91ff96aba14d73c000a994312a9aa7a74353e410742d079aff74b2c6d94577c32ba8649527f084b0eea4d46872e2c0c1845db02d5e0b747c852bb779df2261be0fc15682a77bf287858a3413517e03339d8154ad0bca8201e2a4674a8bca706972e2b6de6e8584974409a50201a772f88b3581dad0e5c315a647c4b5c8e5646f383b97e12bcc47017f2260227e1a94e5223e5511a2c8077248f3d3add7aa45b52e89e3b3a647a42f8fdca465f2d4650de59db1688ed7f58665e937d29964e5e475f81376d4f1c33400b97388183b6353cc4c07477e3803f1dbe0a740b1ba053c1d9a377345c656c1fe08f14f2e9a81bc7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3575a60ad292d8dd82ee3843410a144be6c8915e4530f0c0c18ecd4ed3c8be7d11d1b17357742b9f63b8a034437f0addd1e87d2450fe1ceb66424687f3a880dd850a6636f4fbdb2531d9584581b404c5619c4c43b3565ee6128854d813323ffd2321e3d924fffdd5426ab4e361bed22b37d8f1a7715aa493d4f3f96d324d4bb244c551af63bb990ac09d2f18a11dfd1ce407c05fc3524f2c1275bc92d0bfd9eb601f4496230586290581f08746d7ddc455f7dd5b300f54893348a86603d65e3d62593d5c34d9096c925aeff063deaecac2ffd48df60b3a2033eef7e693997b684564f823d42fcdadf7e11e3b924cb56ad72d0475446db9df8189dbfd9651e55656d8475c0783642ad5419a1c36f2225b000908c8209af2c9b115d588e3d168035074c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357c737ad060ab28f574a10e706fe26cc3df991c33cbaf88070f477ec759e78963f83bc5d79a920b538327053472213a60f33e1ab6e9a92763c2bda392f236ec52b00fa9f1863bc2430aabd40377e7f7e759c58f27df0180815cefeeb45db7ce72e6d83916a576e4a1d5a5fd674689d462f7f9f0675d4be2f304c04c818293d1170e604502d3a066f5bdbdaee62a00f7c24c78dc0322fbf901dafba170b95311a28f29b116d0360f51da28dae7a26ef5c573a2b973db96b7c6f9567d14fef2a2231b175424dc704f5169e172322c2a07d690575e91b4041961d14ea787112b2812384b30d692cab0e4888b6f35530ccdc30b4e30718a2050d0809c04325fb2f5f7a9289ac23ce6a07644ffaec38d5e84413f01b7b52893fc05984a5fd5b21282524c53abe084fff5d11d0427c5ed43bee545582b43c0309912b7e0a125a64bc403475b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956434aaa2131dc3f149998f6730886cc562b1d17d11071705272a71eb4d66af01627947dc52d5d52a500494c34368aa8c5d84e19d1c86a24b4e965afa03625783275b48d5038ba8ac232723696ca92f3127da19dd059ad9cd7c1b8f6c16c29cdb77d3f32d70424fdc37449175622b76896d9f2e7207b84ec334e6c93225944d361d8773b6098e6a78391e920102e9a6c83b3be6756a23297300d972fb2270e70544ccd7783da898b97b6e4bfe23fd76e55415f92e510dd8051e34661d0fd4eee5288c14cc1a9ccc490c37c559112935621149d650231f3a384f94a6827697b4ae35970cf71f71a07f1891a89036bd7b1b0351bec86d2a12092d0b79363f581ff133a53d4b53c69f962e1a38fa12f32a9d3bf6cdb63d86b4192dd5e93f0b49ba0760bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995643afee92e318806421815611e8e2d533a37e55225ab0c710cd34593625713444b014b493046a0df6928aa6967d1d44902d8af890f6c19f46cad0d8e3ea3e95c27c379eb136d51081400bcb01f3bff44236274e673105246341cae863bc1bec4368dd51728f6b10a2a3e88343762b1413bae2f1453f73d123ee3d1945a1b309e17a35539795152646deb83434f6ff33a023a02a53a924e5c46f1c761387433f129de312e1b8adc1a1235837521171188122838147e68cb1a766b135e068376276c5dd4c363735c9516a42e4541e39b91536966397dfed5ea6b59c7712c073df35776134129be91cc3b6479a217b0879d5864a3af48becd831fdcb0c75580214d059654745457d56c6c42465e124ab92121606bf42560f8404f8096c8119b2b4b48343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357afeb8728e006fc273725e21f49a25a0fd1af7357cc79ee32a6f8e83a875ba64d4344f1260159d733ee3adb79e44fcd616568eb7e38863d02519eaa309e7799038dbae45e14c90f05b579a61caba1e95097136c6829cf7a3c40c2657a68865675c286f55014f6bd6077afa1616887a2435e76b12071dbf21be024371b5c6df30f3cd7c9541ca3ad65b7e48d0725f10754395cfb5a25c1a8743f71e543b98ef0004bdc4f0c0913c565191176571d616568c9deae59ef5be7171e05c07d3b219304f265630c361f137196cb276e503e6c1bc725a16381bd684168a08a5bea27087593ff5b4bf8f1e12f3e4faa775c19c65ba33d4417d2d0a31d913ecd477349580bf0fc851582805202d48d6834747ce0064e75c950a31942540046974e65b70a01d47568213a89630c31aced59c348466f978c9b539c0e0a6855f57f47134cfb0a75b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956420056f617282ab165ec6e734733f0b20e8f7785b3b9cce5c11cb226654d2e1508953af5d2bc847460dac5167b2145e07f5fae34c9910ee4492e91f4db0048702de76fd46c57cdd63f99fc14b63d8b0676f76026979e46f1a5295513cfdde63032144161c6378a11f3652710e71ae966f1c0d673af074731340bc3a1139fed822815357177a77661e9a651f6ea4e65d096b0ccb13df3fcd79b7b81f56aecb96717ed3a06133893d4fdf337472d44c7e38551ca63b41aa9d026ee96a24019cb55e7f79ae66cf3f1d272b527467b4d4c80ff6b2000b58e8880096516d4eaf250307e410ad3d4495081b10f117761676417cc299874e9c3eee0ec273d3444c39382cb9dc990e0649c20c8745be4dc379024f1f156355edc44f2d0bba947abd14e23c452acf236585013f9e99821ac0f51a20ca17aa70ad62204ca78c4b66074f0d15e5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3576a05535af2544a009bb56d0ccce31627f152541418f08008294a202a49eea15224b34749a572471808215b2d23444c6c1a50bd51b507a77a106e372362e4680df2624d38a134b7724c752f457745d879e4c7204c51127a7cb755a35a5c74a736119d87319262a646769dda6cb9f2185ffc034e07e562cf2792f635176fa1a21ff4dfb70dd729ea259650f92c12a5f136de24b25b95b85e7bb1485859ca942817cebd6e329d500a658ed2956dda93132137781f0cb50cef1cd6bf5e07a7732f0a3c353f73daca90381655cc5b9e53714a5a3bae2db3258c0a0a810e12cc32484b9cc13f148d72c01295c4117bdc3c5c54a9df474ee6bfc9026bd9482daef255219f5f0e4cc792265111c5d152eee0ad6972fc8c2df5dc84568afb7831bffc372c2b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357028dad57e8b294647ff41624f10a1a4054b40b65e5bd7f3673e5871cfa88ac4ab05800306c9b25536c04733347e1c40cc2bf1815f4f8c64e73e5641741b4111b7dec787c3711e5004d16b866a9d67e0fb7d5f84d9d47504f1d5c725b7fb1d903d1037759e59625312252b533e14d3c1c2e74d802abe53b2a2dd2f876bf74751439762f5a5cd2620a39350a6e2d19a203d9233d78e9f04b2efcc46620f6fad86b1d44272b878c4d34d4008a47ac40ec7144794d7233c5405823d49a6e642bdc5d7f79ae66cf3f1d272b527467b4d4c80ff6b2000b58e8880096516d4eaf250307e410ad3d4495081b10f117761676417cc299874e9c3eee0ec273d3444c39382cb9dc990e0649c20c8745be4dc379024f1f156355edc44f2d0bba947abd14e23c452acf236585013f9e99821ac0f51a20ca17aa70ad62204ca78c4b66074f0d15e5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357210be3641e768a6fc3599c50cb77b853fba4b45daf72e57b5db6403a8535af5c876f790cd8a56c30f9b9776f0f656d1daee7c929d5529d596c19eb7a8a4acf306259d36bf6d7733508224531ccdaff77670e1835a41e575ea5b8784c531fb33236e37d3cc4095e2ff54a0076c85c924c0550932f7a30ec1cffb62a2aa6fc5c27e2c0732041ab070e70fbec47937f056b4d273511d25e183b0fc5155286872a1b2dbfc660b9f0f90e1f7db76f40e43c47f336a716e0ec8f44a0938c5e404a713ba63497764633561584c8351282b8017760f11d4224b27e4cff05534d3f97356972c41e50994fbb0d1437c7539921ff729cd399222a6a8444cbe2376b2c1a741f06cb964239f3fb1ae72fb50f4488075d57eebc7c7e1c3d691d047d366a63ef4c386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995641fc5374be9fbca35a8869d42a3fd12249f10c6737a160e0da907ae19bb265951f5bfed4e015be3006dbe0b6300589b4e21ba6e4c8e81635e6250b75514f24604c5c9dc55ebb59d63ad063b3ec70f3a089285f95a6c0e2635b7c6bd31d5d75a1aa9a8b8440c6c895ba26fb162ea617f0884a48c2800190a3eb41c130b1f95855ad317e6350b305867200a0f4e49676d6dcfc9e34d3c96197ca4c8553497274412f1e6ac04d992540832cd4e233e0aa90185e27a01acdc8c37a7d1dd2a5768f81141e35f6b0c27593c1b2088626c41c64cb39c5e0268bd6b341b86c621d81e623d378caa414dd19518ca3f884f8cb1bc697b502c17b9297f4faa0c3c53c537475c36c50b0590afaa39a7f6205049c8986c20504b2f07603146cb588e264f09ac19c7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35769c3b532938cf041a49988630765cc44c55f5e1835a5c94df0b15f22c1ac9566474af25b79a55f679685252dcab1557a584b56637d4b7c7d03994d52534a65655b4229656e25b12d9b8b2c1a574a863426a9162abe665d7230d8d550768f5374e0dee9099599040317bfc52c94a0e1587934bd234dfb455aa415e16e8a24cf0b2542b3724ee20257a24dc04a2c1af665f03ccf7cbf30ca43f0e76b323742db37d7888b02f0e62c470e83922ccf28854c5abbb80180b50e12f18ca96acf14e1566c210c376add6c1820289f1bbbb153532b4f751d1243352838987b0581819b10ff4ff120689cbc538828512ebccc4744b613bc234eece43b16344c134af28d020b06ff0ad5a9a872cf09671d4386da25a8662333238ed922417025041b88401dc3bd7931145b6730d1dd060b0acb520ddd8e3e24b64f3379269eae25101b646f2d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564f6892a1d541504473e552905cd9c0e545b94051b26a3eb0ed98874590f2d8d1c9e1a5a559071f307bf031817da30a17e99fbdf563636ac138cbe0f261a083e780913422af1dd842f593cd83b6477e44af89fb6784308bc6298a3675f8aca1945884652451b2b810a7e03a97afbf2537d0463e040fa542a55dc64b8641a925e01205edd128e08dc087c9eb57541a3947af954e96190eed634efb840684b43345d334714466d2c01626968d81b09cb8f76999220243dece85c2be48c7d6850c824309e76648c16901243a699176f29ea380dc7a6700a6f681ddbe6746c5d597f6a8fb4603dbb1db659de06b33008e53e1abde3471680d4ab23699be9776885fc18fd71ef24c0a6500b892e1f69048f1327abe903296c159966b38ae45deff8be67bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564b03a6c07af29d94593059c10060a310f5653d90f3ba3035b642e545e8536fb4c17ec7552b0449762c0f1db05d16a0a6214d1f1337b50111af8323e287e96cf2f3d34fb53e040ca542575db5709ec266ee416ae28261d38351ce7676a98175161110d847a2573377b146a137350e2ca6670d03f36efbcb378a5510b1c7be31c5e0b8e385b57a763260e94f823f3ef3202c6fdf05a4f57754a492d990c1b335e30a1136c3336d4ed0e26351659c4272c004c6ecd56f6f93c582ac56d4a2dd70b3e5d5bec6c44f9ce28039aff59506a2e6ce46ff4413379b21bf8a06e2e41dcd1264485d427171106655b39a97d65dd865038ce2c22204b771bc357b87e96e0697dc332bc1abf860c1e81991041a75987081f065e7691f4ff2e9e554c562b7293659d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564c36c9018565c2b5cd2d9d1071edbdf496b0e001f6c5b447ca104b2100f22ac2662709101b1105f053c3aa1405d59d55330789a575b4a0513587c8b023f09973880fb533b180bf34dac6edc727c9d526be653bc2260d1ec484c30bb0fe190f3335ee260679076d416acd006098e7e5f791afe4507cc84f76080681f41d49ad51c105b8d2b3bcda20536090a21163b9b6419bffd048fb99d7c87c113739509073f01c5725b1283c645a68bc77101ec163252661e797f9ffa366029a609656e4632529e625d727e4c4b902f65219f63464a3bc8f43e9a0c114fce47fd2542282d6575026219145970773ddb9a1d8dcf9419b2d39a0b2a48b60f0d12be49d9eac629f77dd474e65b2c3d45c2210e834c26682ee0a60806609e01609d1c68df383a412e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995641a9baf37a64bbd377a578577e97b2b5a33bd733ada36ae7ab348b80a9eabd81d58b43e60406c8860e640c46d0a4d6a59415b1a4b892e2e04dbc97805843fa2588eb0bf12dd69250433f96760310ba133213ada2ad20a82540be10f242cdd5c51a452e562fb4c353d10203c0804a1c17e9ee5920ea044ed25b3d0d217e98a2a5c3ab08d046feff171b6f06112208df90cfa586167e7d53054c6771d7d352f77535d0bcd559556ab2fff6cbd1918962e4ce1e7320bd7744d41c0277a1221bbe55d0ab7fc4323f5dc000600b6114a1f0e77891b6466de19760f5f4b57045e27862c3608e86a71752521317e1a42b4dd3918906aee763814bc739f676e4fe4113d069f5f0e4cc792265111c5d152eee0ad6972fc8c2df5dc84568afb7831bffc372c2b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35761177d582dfdfb6c6c29a27241180525f48153425b24d11fd390a61c61137c55b8dafb7dafba5c6b08698b3650a08f54bbc9c139c3a7a71526a9583328d6924913a4133c4b695d570eaea3285321c43bbe9aa10ed5e9a368805e8225ce38cb3679d8130b867e9a5f00d00b70eec0e07ac13cf4416616090781efdd2a0a58c5067d31e60de4c77a4c25dab32e74b3db51d8a1634e144653584e54301139ac7653d95b453d6d2b246d40b8791e2d157a17fdd07b38f698a0031af3345a1462cf4eb2715e4246d5b644aa5527299aa42c02e9b7775e4730e470fadfd2701f53644f8d67987018f3f164c3c6915012956f0b979c5d6dc36a630daf1606098d500a7bf0fc851582805202d48d6834747ce0064e75c950a31942540046974e65b70a01d47568213a89630c31aced59c348466f978c9b539c0e0a6855f57f47134cfb0a75b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956449dc9e2a7cb6260f112c900dd0f0b9257cb61e4f83caa0006c8b803134742d04f9c0d56d6701c3617c2f4808b938291b72262512a9567a0f617ac4004afa7b1ebbd3c343cbd8ba170c30e33fd0b17962a3b45e6ddf2dfe7a3cf24f746e1fd22243c0e3770dee963aaeb0ee1c36509c0f2f52946ab5d7a2697c95c751db26c938f9de8b5dd80663450db67013e042132bc464b4788f43977ae0dd4d4ed7e6435c1510d673fdf78758e0e8f3156f5182780038b22cbe84be6227442f7767894a4129ba8b542106f7301371770c55d03261f501ab68459ced2108d5c83272acce3593b3196da7aa9410f21d3c618b3a0375380fe4174e5cd20239469a1d2114550dea75c34b4c11e356788fd836f65ffe75a7cb602fb17c07072511a965ad84562d452acf236585013f9e99821ac0f51a20ca17aa70ad62204ca78c4b66074f0d15e5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3575a02e90b4ae41076e9ad355323301f06539b972da5bb697a8f708449363785404229de523cde4d2f9459382c2f2cf957bd7fc2627c77b07de6991d3ec4de7a6e68722f14f1d9da11ff503c2eb761325a51f8004512139519a3425f4d0bdb331bad74447514c04d1afa67362506d40a4fd260820625797e47f7d7ab389fb35a4e5d94a42e00f670331e33c96b8d4f0b2c02039f1e021b375297a298198b940461f5d3861a308c483f5f98456572bbf4799fa4ba63016a916b82dab22f0be9d6605a3ff94ee4722537a0518c51aa741f7c4385c876045e2b56d01aa058578e375fc5ac59530c9852018f86dd3ee7173c099f22740bdba903759082441071a75e4101249374a1da9f7cafd9840638324d5f67502439d2000d3ce64b0b284c4ff342c69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3579576977821b0fc01bd965d023fa0f6767c74fe7356e447660770414d763a7613280f097c865eac76c504e13ec06cc854cac72457e3bc416fe427ca5b215ce31be8c6342e126a5c24644dba137a16d75ccba79f6d38944276cdb8887a6c47c06009fd785da9783d1fb52fab246e7b6c56fed4140f6433c07251bbbc7d1920b066321f9066d4607605db829d4cd2c1cf041338b55180e0c13944568516a6a2083d8fcf9f055bfa30495ea3eb28f38b65096029661fd66386718cb4f42ddef1113f7763141149dcc746c9fcd178260e3545eb70750c76445a578edf571375166f4fb69ded37cbb32208e417d136803597661cae3a2ed6e3b075ff50c81c364d6311c332bc1abf860c1e81991041a75987081f065e7691f4ff2e9e554c562b7293659d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995645399457b4687e357e1cd812b47986a4e023d250758c1d646b106dd0fcfed2b4109fc5a3638a1691b69757c759f018f33b6fc75616039384ffa55d0202841a06557cb2969c363225d84e91d38523e625943d4e520331e4b1fc01e215de510d55548345f18718d09333ae0cc72045035243851fa52aaf7a527c2500e5b5b0dc56dff01ea1b02c3386bf2ea3866b2031717208b6d32bde6e95ca429104b77e9c77091317010f431902284f60008c5bf38251405a2148c26cb2961fd1965edb53816fdb68a078f23570aba26d15970d5c370c9121a2228a2370e41fcb8695da58d68062712046c98710737ab6d4b0f01b26cfb73264d79ab0a5ec2d876245c8b8379cfa4f358c32d103eb855981b8c10443fe1aa6234d888af3dc5c61a04c3de5803cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35784779e053c0fdd51938c200fbaabe03cefdd5e0610b7e229b5e4b320f9710a6ad3d68d665b2cd20046ae4b5be38d880a793b2d1107345222f71d9929996f536b1efd624da1af9461580eaa3584368162d50b8062f23c161902e978504ae0db1fe3eeac6cb26170691b7b634764dae24e0842e855fc99f4746a1e6467d9b33e466abd470217e693100b1bf724a3a6e931852ae56bf873c71a08918877a7bb6a6a1fd6cd40a09ddd073f3e6a4f93694b6d7ed8ac623fd3d4750eba4d408701d05babc1100f76e25808eb47835e0373e53455f71b5de6e40e1d6f10e25b447884390c845875735312300bc43109963348057d70e9493050816a0e40836c45eb872625e26266e8d42f2f4e3b97094036b24d676dbf64b5f1d1404d934c481d122b762b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357b28dbe2fc1edca00ce5572398fbf6d69441e2b60bdfa9a20ebce896cb5ca954164425f4cb2465a2b556680188e6069361406f27e2518e75f5c09466f3775b92de06c7a02319ca25288b9cc179aeffd3f353f01194f98045109394c7a03c09045638fec517fdec4375ec7993b9061cc045a23f3573ea3eb0082c22301d69e102667c09f2d34e5b44b911f87196958831ddd4c7b00f3295537404b93339ac5231ace999f2e9adb906705089745f8cbfd0f8a4aee5e621bd8077d5d350a996d7c05ef92f616147f4b1ce53fcf51cfa4401b18ec072e3e1cf047cd203b0d0d75ed2e19dfb42f3b535d022d35860b8be4970e6d09231dd82397323db28b1cd97db8399289ac23ce6a07644ffaec38d5e84413f01b7b52893fc05984a5fd5b21282524c53abe084fff5d11d0427c5ed43bee545582b43c0309912b7e0a125a64bc403475b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564d92de92b1b8c6200486cde1111ceab28d8e49b54f5e2387dfce6330bcbe4815e0813ee1f79c6bb04f7c4ce32b383990e86a4702528c4be31a6f83b61c68b431fb962d73118cc903c30991c66737f9a43919fff7c78dc5e23e9bab84d039fcc2c03c04f62b5f6230d93eb79133a7249078b09624504a7696cc8ab777489fbe76fb7735150f7c38a039e49725a7f568d3da1363d082f2450264360c90bba58d6612deb6420d8ed0c1b9076094285bf752f539af8371cbb1232420d9e3d589daa4ef76ec62836c33f413560e51558b95f489051cd3e6e90d82b6ec012447eed0863b69ded37cbb32208e417d136803597661cae3a2ed6e3b075ff50c81c364d6311c332bc1abf860c1e81991041a75987081f065e7691f4ff2e9e554c562b7293659d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564a22dad0ef797856f5cd2266a37f0895cc8fa782cd734d84f0b6a8d7835cd08543feac35e5b150f3e32b45e2065c2de0a12ba6463e46fcc1bf51247112177c77296ae192155d6342e9863f12d48fb3b044d816352e60a386eb9ad76799c9d2f51a1d9bf014c55315f228ad5658275ac77ef1c16703358b75b2f237107617ada7b13529935df3c03631b8b2e0ef9f3d54ce38dda7961b3572cea654c7256377e253572103857930d555f0f9e30c6763e42cbac0b3e208fbc6a7d5b7e2adef975597926b65dd264422c2ac5747e369f0801814805633c21a93dbef8af207aff4668ca8779613ebac1594b47507e4543eb0894f4633effaf2b4c7af4203ce906a6012ccc5c11f01e933c764c771cec11c661baae67618567b110a00d310081d0db130198ba3a66b0a6131254b570cbbc44359f47067ebc15520c5d8a3a791b5f7d772d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956406e9f61e008add67b0af1425c4867e6d4366e20025625e1c289ea44dc9084621cca5e27d84ac9a4c91037f00634998170ab5bb47731f201fbf9a747439c6125aba395a3a0157c86e323da87ab5168159b880a93e6f3e006db4fe6222026ceb580093b26fa8616550fb3f8627ef7f6465a7cc1b2423fc8471cd89e31eb66be01ccd1db34c248215783736023a377ad22a0e510e2c5aa216370f737f6c229f655c020bf45e61c673052119445946f10d786b57524416ed3f2f3f7ae668bfada62d83f3fa118183e8618f3bc70ac7587222eae1d7790263e929f4dfe92a535f801acdcbe44c042c2347ac7b4142beedf074ba7aa119aafcb157c0a4de76bb607b3283241d1870631e19133b1b670f76724ead8a021ffeb5a956aa8fc25e0db76c350198ba3a66b0a6131254b570cbbc44359f47067ebc15520c5d8a3a791b5f7d772d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564ab43697c7c9bf453c6b7b07ee7a3566d35dd6d23c649e85da5904116c35f9a3a3d687d400e4fcc06d41db31f020d0d12f102343b6d49f64b19eee7001110083bb7924429bb0d6f50e4872239f4598445ce7ffc40c7d0e7317ea5d54599447e39d2af671fc3971216a45c7c19c302b57a8af22254673b1815726ede659026ed3a5c725819bc62ed500344b67aca506106ef797162a09b0e469cdf701ac1de5e7cdbf7b545ca1b8e71b7139523a391b91eac719019a99dee0236de513f98cad470cbd1974a1dd8e160859a422e079e4672abb20b52ebce2b6dbe410f24c0364631c0c9d871a2aeba23ba779f31f3dec5333422f95007250c16c760697ab07f0e79abd88a22eea057717f1775723f53cf7aa0e588066e950c6629dace69577cff482e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564d9efc437ac1a4c0b12db640b4326ae200eb0a970335e867c660dcd599367bd12c306d6230b22c5592f5f8b4468242a19f4b1d32ab5e4041b03b0cc3c8f552e11e40d905a6334f425a6cd3b463e304f7a2e10f16f3134822126635f35316a3053fc83f103df422941b777a06b7d008f6d4e31ce0bc6cc0d2dcdce85005a6509428168b56c1fcd681687328f72f25c6771984820406453466d9be6bc0374e2f24b334714466d2c01626968d81b09cb8f76999220243dece85c2be48c7d6850c824309e76648c16901243a699176f29ea380dc7a6700a6f681ddbe6746c5d597f6a8fb4603dbb1db659de06b33008e53e1abde3471680d4ab23699be9776885fc18fd71ef24c0a6500b892e1f69048f1327abe903296c159966b38ae45deff8be67bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564d1282b7d8b2ba32d1b5f9f65e8782846eb363d524b8454245b6dfa783018385824011904221f41260c3b4c41be509322dfdc410dcbca15003d0d5322da84fb020ce55339f76d6054cbfa413946e3261d4ee0e9140a7b8d4667f84167f83bb523fc3d86473fb2604cee019865085ba81e25532003c62dd70186953a2eeffe4a3ee1ea721267cf1f0badc33817fc59b85a4af2154664b545054020e3437b2cd97b354bc72535ebeb29290d401557a173480a8e5b0c4d4a0130ad1ba075b131123c701752025c4da778b41cc870fb555e3f27f70548a0e4a015f66cf86f65a42848ebbc07435c05f801f7f53f7c7092274578ee7e354ef39225b443c55acc304c4c8475c0783642ad5419a1c36f2225b000908c8209af2c9b115d588e3d168035074c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357ec6c9021e8f6723afb226b656889c47541b42514f7ce4369e3f1f26a62ecb04755a560101a24ce21190d784866a8525ad04e531493ae955c9b291d2fafbf8b442802303fccbc035a7c38ed2e6ac6ab05d4e258339096300a447331141b97b819b0d493289863dc02daef191c7a7aae4cb9c7281a07fa802a5d145f482733cb79797a196daed6cb0e193b300368ad4f3e153d724dd10cec0a611002547b8d4468d7e05958d803362a363c6d61a4cbb637c9bf5234d4e9a94ef467e679a5b7b235073d97579be8623578b66555055c7e7c24e71a7a3b6ebe3273c996629af3fd26faedcc164a4c2c48a4c2fd0b9d25c542f8ce925d25c80d5dbe1b5a173d64b43906cb964239f3fb1ae72fb50f4488075d57eebc7c7e1c3d691d047d366a63ef4c386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995640ea31e56be6756304fa56f6bd55ab771ccfce42d60153c6be97ae07778a7f86559c3e34da54097346068db277a739e0a22ae1470df3b284d9144684373fe457003335c02037aa474959994518ace227a370c0357f467aa4f70c61b1a2a79192347b9f62b63354e725150d36ee30e051412062414e9093b2694f690443e69d556cf466639a8abc4150570ef70562c2819b42b7031a66a2b547ded554770b1521836268d02ce3c0d0b0a4b597531814871870fc13a6660b41f3c1541754c88772d77a33763e66a2446ae7273363efcd0572be54148b5177a74d810a973d5fbee30b99fec7cf5eb5f5339362442e71633317e26234ef4a2c447c6c7e27663ad352627415e435a2fc7365019553b214e0d285b2083062ba60d35536d51034d13af1dc3bd7931145b6730d1dd060b0acb520ddd8e3e24b64f3379269eae25101b646f2d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564e14c904b265927218c126779a8080d448abd1e2dadc690688440390668d29103e4c81849b5bf0360718c58638618011f253df2080a8ea159cddc6d365242f5708fe07d51e582d66af52311687e60933dc1adf96de082a13742631f59884b33233fbb9e1420775a527b1c1e5820a807329e25f36d4ba4bd2c99845355679047202c2f1b5b7d62742fccd69205682c5a3374c99054cd815d6e6b4b2a109faa753891317010f431902284f60008c5bf38251405a2148c26cb2961fd1965edb53816fdb68a078f23570aba26d15970d5c370c9121a2228a2370e41fcb8695da58d68062712046c98710737ab6d4b0f01b26cfb73264d79ab0a5ec2d876245c8b8379cfa4f358c32d103eb855981b8c10443fe1aa6234d888af3dc5c61a04c3de5803cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3571690d470f0ba645aa671c367c9d2bd60936a5425fda1ca08edc63f6c703f977133a704247bfe7c51e4206670e9699b0f3c95355d75e30f1c857ae83ff67aa500c5381c6e4aa28871797d782dc6bab0143515fd28ab65fa05e36deb6ad4735e578c29216201abcc4437793632c37cf657726d0b115923ac65cbf6b752cf6a9217fdebd807d9cca33b2842ed6c95e09b3e322f785939e4ed44ff749340d40f06668fcf9f055bfa30495ea3eb28f38b65096029661fd66386718cb4f42ddef1113f7763141149dcc746c9fcd178260e3545eb70750c76445a578edf571375166f4fb69ded37cbb32208e417d136803597661cae3a2ed6e3b075ff50c81c364d6311c332bc1abf860c1e81991041a75987081f065e7691f4ff2e9e554c562b7293659d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995649f42567e182a2c1a0f6d5e4182cdc330e8daef286f4e2f09e90bb64678358f29a525e9172da32d1d804af533367f8111c501a628422ca25dc09eb627954a8c01cda4d8693ffe622275cfab6a0489c8687efa023276ae3d21c3ebf05e670f5e168677115830c0d76eed27443b829a284caf9ba9038d74711dc624fc5e91404254d723a73b2c70a1621728e51a69f71f775a669163e86a1c775a253b6e0c665e101a6f397ada9abd224fbb9c2cecff7c1b8aada36093e8ed057df9f64c09f93c02fe68e46f9678f613df327215ae87534edcb949178d00511e2457ab0b3d6a1a05ff22543aefdf8f5e4789a06ab3f76e5729bd484ae81fd465e474594cc43035321a350875aad1295259b9ec455cbbe06feb79a613ed065d0a16779721979b60234c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357d6f9211213091718d3213133b399cb33469aa345ecce1f5a536cbd53567abb3a1601e76e508325262c03bb2ca074480dbf7d3b5eb83f80297ddc4711196c6a6b19dee86091178d418dbe237ee05e77052d2cbd55387af92b30d8fa3f38c07841e74bc055940b401dcabdab5fe82d622caffa5e16dff83815f9dc9403b3672f7b6f1b2420d21b4d797a2d8e000dcff174f0aa40185cbe4450ab6ce73272da0c40a1ba9d06397a85733eaa9448c87c50697a3a1b50acf9a7683e280f2d02e6f6472da6f16bb00cce458380ed3e73745529eb5da96ec636ea112696820b37d3335f65b8663d483f0f7dbb57d262d35e206bd028dc16e9138f1e79d2724794ab2f1fec4cf84519a4d658f5bbcd7a5c90b8541f61c756a004407176ed47702c62d072cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3570b7f1203b73ccb01a7743d4346bf22736f86336ebe4afa076dfe2820abb74666055b4610390ebe1c753018786824ad735ca01a35ce879c5a6d88c1694fd7a24062bd947874e53f37637bbb586e18924257a15b7b442d6c30e4a16b721de04a5dee71720240375b72c80f282caf4ef638d07259462d83f13110c6585df5a2da58803c5633964ad838a5aa5f28442d6729abb81c26d1b0337bd09a5255ce0a9843b76ec60304b9b77a9e6d6b4fde041862385da9605928ca00361b6f5d70fc7d6a74a7d43631ddb3224459c63cd2992b11f046e840828e281435d64408c8e0be72c7dabc30383113111efe780d94c5d1168799fb2d3549f71538adf66fce1adf4b1a350875aad1295259b9ec455cbbe06feb79a613ed065d0a16779721979b60234c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357beb1a83a8b9b595a31a76c0ec11f931d21570b35e07c69377980344d3a3ec93b7f24090e9e8a037c9ad1cb6a6f190b3f641a4750de3e6816f96a191ca5706e0181da71777f9b782bb2d22b639d128e419e66af21ce4ed425baeda32629296e0e041ba85c65d4a914a5747d11914eac387759e24efd47151c9532de5c86894865bf9d1c10e6c66d26173be3524a0fb07886fbc66b48144006a5fa162818ae857714ecef54047e9151861ccd4a03335a150049060b88c7fc0c3966a811dc407634bccce8160a12190a1d9e5b54b0de2127062c6d27806c2c1c66bc4812839d5649c5bed61bf9178411b95df3292d882d75874b377339bb983c2238b2273265513373752d6889912e3111d0607d5d86d447fc2cdc3a7cc3e80c3684d830ddeb14109d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564b8cba4419ce5de361da699118183cf3e7fc9671bff88351d92d45f128e7ef0482d9c3c695284cd543704df46067e0d7a2b4b7651cd00a5242fea48085c42f74d7b215f2bc95f0407738ed047895948721e8c7e2a147f4160c4c32945f2437a551a1bd334eada996d27ae766ad970cb25686f2f57b8c4945debe3d60e66b42e2831838b6ef0008032b453e71fe9f538666414b92549bf8e5e5a695d6c81632c17434f4d2f8c337b0add450605d4f23f7d9e3e3008a968fc7709d7a81ef458a81b529e625d727e4c4b902f65219f63464a3bc8f43e9a0c114fce47fd2542282d6575026219145970773ddb9a1d8dcf9419b2d39a0b2a48b60f0d12be49d9eac629f77dd474e65b2c3d45c2210e834c26682ee0a60806609e01609d1c68df383a412e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564fc516e156324911ad990fb2298289d4e8843972c2dc024361b266d228f51ac485403ce1e60f2f77be9cb974511ef835ea64ea5738eb84415f6a5652d4e9a1a786ab518629f86c3319f61cd6c8fea4f6d4e046c1ed77b825d37db192071fcec11f965fb2755b15c55e5666a168482c459813e175c2452581d4a68657355ba3e1b7d5c0442844c8b7926fb2b119e02f767d41adf1827d2293a66de6458cca3215004ea4957fb30cc6ec4d237143270ee5b9518e75886d2d8213c8f1f16d9edf37c2cc38f0688a028568b8cc84ecf3b47680c326f3655f0f546e4e14a039f83624744b3df30bb6b972d52bfe95efa9e9c26ca5b5a63b8f9ea7082bc9a54ab90c54457da191628b8b9333b1c251ae332fe4468b71d0fbbdec631136225095ca68a30fd1bbb115fef2e799b81f22ba03d86222a34405eaaf683739d056807b9960b60374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35739d57518e5f34c735e47ad61f028a841c3764f4808dfbb00be57886b9cf7186a32544120ba503033b83cca7aeb5de60a7efb64532a033a2a177ae832d946dd15b47e1b00bd7fa157e209043a36e516017a3afa4fb40c9b6d4b42ca255cbd3d1b5068b178db7cf6294b50b958ac5e821105ded56b5599b856bbcf075ca4d64a71c9ce33078dd77d51b98c8a77e2784a06b5369530b9d6361641d7d22568c9f02ece334c0a80b21f2e0752af4bcd44a10938a23356e110f23c06b70f48b08d8d75309e76648c16901243a699176f29ea380dc7a6700a6f681ddbe6746c5d597f6a8fb4603dbb1db659de06b33008e53e1abde3471680d4ab23699be9776885fc18fd71ef24c0a6500b892e1f69048f1327abe903296c159966b38ae45deff8be67bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564efff7e00bd9ab053def037405b04f53b9b299f3cbea0366c1c29dd22527c0d70b7999d0da97c014d51e3831794cc611b9bbedf4e07538e304a27be77fd88b73ab17f4a561bc118337b159d6a0e29007c0654ca24bd4efa36a3956261d83cf5253b4e843bc26a59454ab610510da7f53787e43173585fb05eb9e3c03c98f9075dfa18bc26e3be4e02923b1113d81b414abf4b453ffb43e251690aa90a9cd384561864410b2521f0100f1e9b69ded490578bc40925ab6f40309cbba46b74fbf67b0d2244747bbbb5473461014fe758e03e0a1df711a85216192f68253dec06b407599d4b17b0e50d51e811c0784541ef32019c90405f6ad536e5000e2fc11ecd17a53d4b53c69f962e1a38fa12f32a9d3bf6cdb63d86b4192dd5e93f0b49ba0760bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564a2807b7886a4d158fda8d71d7c048e697f486a30aeb1634f08655e6a7f5972294251180c57d91e27d07c9d3729622b74e4642a4875929d48422c77103be5ae6d1fe61f31a7ded61dc05d96530f01bd401ea04d5c5d4816298ee4ae13aa88782a48345f18718d09333ae0cc72045035243851fa52aaf7a527c2500e5b5b0dc56dff01ea1b02c3386bf2ea3866b2031717208b6d32bde6e95ca429104b77e9c77091317010f431902284f60008c5bf38251405a2148c26cb2961fd1965edb53816fdb68a078f23570aba26d15970d5c370c9121a2228a2370e41fcb8695da58d68062712046c98710737ab6d4b0f01b26cfb73264d79ab0a5ec2d876245c8b8379cfa4f358c32d103eb855981b8c10443fe1aa6234d888af3dc5c61a04c3de5803cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3579dc8197935efeb492df4d60f89396a0b5a33805e35d4636405feb56bbbb8a80f053ed82e62204369e618da1cf7acc54065fd231cde7ccd0e32062f2661cfe74e379b5c5914847f4936b5df2fcdd4681d09a37664150a6f312ae9df23938cd15a0395044654dc5c3d3173df749ffee625025e542f7e923d0d9922a806c08f4f2921629409bbd7d753b73f0a3ded9af47b28d088331472f14514bafa159d1c0f3ffca66335d835e8496892bd2798475c50db39356838b3f217f3fb0526cc7b5c6e5a3ff94ee4722537a0518c51aa741f7c4385c876045e2b56d01aa058578e375fc5ac59530c9852018f86dd3ee7173c099f22740bdba903759082441071a75e4101249374a1da9f7cafd9840638324d5f67502439d2000d3ce64b0b284c4ff342c69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3573d8db5171ac5cd73e145253d90169347748a0c315447361c7465d6675f387c01e55ae064ee6d647cc8caf7664c94753f2a16af5b7605c8108aa3dd7d9c4b931ab47c9560734ad7475076c62c732adf560306eb3dd7f9d654203586064f7d40396870cf4d3896ee3c080f8d1cb84f8058859c383f38ba7b4aa8d442291f426707661c460f3d558038d78f85209e4e953a0f433a5918e6142c6018b912ddea237777f9df1bb82c1c58da1a2f14526e9d5eda27876a92db202e9365541f0844dc385bfd6a18a366fb4a9322de013d8da47d361fa47e1016f46689d35e2cee854f6ac5ac59530c9852018f86dd3ee7173c099f22740bdba903759082441071a75e4101249374a1da9f7cafd9840638324d5f67502439d2000d3ce64b0b284c4ff342c69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3573fe4777ed1c5391f0c71b43d8d0fa029a8128c6c9eae3b5f28987d24fe49436908ee9a7837a1d341d98582483879b843b9607b367155367cc60d3e3a841c6164ffc2940d1dc0f26b129635647a221e5ddc1fee6af7f6e9001253c03c2a16220a59bba45ca045f81ebd0c5e07f6e56c7367e65a0b3cc6a328c86165696ade2c699804922901c1c35aba353750878ef93db5bea33d883be87d2ae62a10911ca43daee44d49d1763772b7d311463ff0754ee13ba878fefd7b4d992e1d5b01766c1da77ff553c54f85104cdccb645ee9175fbc5625373eb27577541b314316018a649cc13f148d72c01295c4117bdc3c5c54a9df474ee6bfc9026bd9482daef255219f5f0e4cc792265111c5d152eee0ad6972fc8c2df5dc84568afb7831bffc372c2b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35764a1ac3a28ffe63c769bba096321696c31be5b63b709b64eb4e5172b9a6de270e3093b655cced922d523574ed4846f12dfbea3598deeda125d1af63509a218711261ef6c4786eb299c684b0f469c63452f042a091c933702f229db2fbbb141536165633a38a8af58c4d3cb5f3d0f275e67d57a4a31694311c0577d738aadc74c16eb2b41369c1f416e4a325510f58d4f02996d0ed977ed5828a17274f2b2a80d8f8c7c53f43bcd28a74e0e44d02b0b5f78537f00c0189a3ffdf47a2221ba417ecbd1974a1dd8e160859a422e079e4672abb20b52ebce2b6dbe410f24c0364631c0c9d871a2aeba23ba779f31f3dec5333422f95007250c16c760697ab07f0e79abd88a22eea057717f1775723f53cf7aa0e588066e950c6629dace69577cff482e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995648c3aef0451d0c47e8b079f541f6a7b64c935563fb4352362bc9d183471d1626cfcf9a307afc666680b9a3d2f6e312d1a7786ae30a3cc0c50f69789600bceec17322ece1041214143bb5e0a6a2fc8c878354ae075eb924367aca5fd06cb923070244ebc7efdd4a86780cb705167211d5410f9227015cdbd54c675a62dbc42c06c8da2030f59d0fb0f0ad29f0db0288e4ee429f84ae602b105f887632e6472d33b4d026024555d992818651e105e8ef52ed3a5256167bbea55caadfe72df00e76b36c6fc17a299956781a1b67b1ba6732b5417b70866931d2a387006249daedb3215d40951a9529e348e9a807a45c4cb4c2fd7161fd6c2f072318d827808f5b53bec4cf84519a4d658f5bbcd7a5c90b8541f61c756a004407176ed47702c62d072cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357e3751c043f0673675d08995ad1fb396a74c3e36cbe6d9861cedac377de34574b19fa226e1605f72d9b8c397d4c42736bd2b7945784deb050e3f424476857d20b3bae537189ac543b2a84c2702979784d09a2c77483c505081ab1d346df1e721e833935108537cc0f9cc7fa4bac315178e6e4fb59ea8ac302934fc71d3f51f60216f9ee70b90dea4b02024a3352ce2a793422e13c46408e645ec511007b11f3227089fa4895294b2f47f33533213d637980acf9648e8a1c1dabf7a822b2a9fc5677a33763e66a2446ae7273363efcd0572be54148b5177a74d810a973d5fbee30b99fec7cf5eb5f5339362442e71633317e26234ef4a2c447c6c7e27663ad352627415e435a2fc7365019553b214e0d285b2083062ba60d35536d51034d13af1dc3bd7931145b6730d1dd060b0acb520ddd8e3e24b64f3379269eae25101b646f2d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956491cda70165ef092d7ef3c05b6a725422ed1b1b4802f05d4ea27f9d103893ff36049f0b761f396a687fa4ed1ad44da56f928a617e4b9da17bf05a974f7a4a7c486a8de03b5801014b56201e026ab7fc36a8b3f614a317705b4a12065245f9733b3c6c527421df7c012b93e514edfa460326b24910613ef607cef9651bcf6bc6412279f170f7087b7d30814a5f2d579878cc239b485fed3773908cf11d3dafe80120572e7538701550e0a0110fa174a4618db5e1766f38750550b42c5de76fe830cc881d5dd059da58b547ae5955b3e217aeca5a126866ac2b909e953797ef4676cdcbe44c042c2347ac7b4142beedf074ba7aa119aafcb157c0a4de76bb607b3283241d1870631e19133b1b670f76724ead8a021ffeb5a956aa8fc25e0db76c350198ba3a66b0a6131254b570cbbc44359f47067ebc15520c5d8a3a791b5f7d772d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564fa8d5f6927ed9224c47d55122d06114d55d093646b59675e2a66a9359f70415320b92056edd1c20ab997fc2837ccb46a3e77ee0c06838120143ddb74c7874b46eaf7871d57b11e6b1a2650699203920cfa16574fc82df8126adcf468c8400f27a4494119f7dd632fef294d151561766c31c0b3549e3e222a7c5c245a28753d1fe8b6a97cb5a0733291c9b825fa619e044b80066d6f29af2ec189fd6fb4fb8675265fc134072b021c4c4282093c91bd52377f115c96b8b228ed0bdc46354b62224441820df0da0c5341ed1f6caf2d01062ccb3a6d441e980b9050447671832d78a294fe49e9fa6d2a42013b744fd26a3d14e149348604c14898587374a7ad4d14fd71ef24c0a6500b892e1f69048f1327abe903296c159966b38ae45deff8be67bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564ccad744551ed1819e59d4852a166c26401a34007e915d2022693ce0dabce644afe9259222c8d54497cc10d7bd6f1223f57816b6cc984884334f76d7d4eaf9f6e3accaf02bd2d7a19a58d9e75c08eed1670b7b152cb59cb3f3564a41b340d4d611e6ccb2f14acab591026a02312ebd0269462e30c5e25db05d7cf9375265c0d661f92c03266dc753cb5280e39c4693f270372aa5b957c0a5e2ece8d15487ec9186b7ada3c88c589300cf4447af7de0f548b90a1770746d703ff03ca0938509f4ebf10356e3a5389145a633f7e84e3af678f0de8211be8f77362a24238944b10295783836318f1316a44ac5a5ddfd5e5444935975967d53c7622d30018d873a03f4adfe06834c0fc78c36e5526f3fbcc254715544874b78a2a71aacd094718082ac69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35757832203761b7e3a76237d4a911d0600844549148935065365a5187691e02a182f277165834002571f65e155136a206b35e42a49c6896a44329a882488685a2a7b7aa307e509bc3a43c0b461d56b5a35777d5712ef664307fb8a9312d4c4544422eda628c2efa82d6c472a7685ede466e35d7f48e08aec1a2af64e29a0e26f1892603a0553491817b731af749a49c74aeb7d44085235b21cfa57fe5c44142a3e07ee262f3081960390749966a97cb54f904fb24d73bfa94decd63d15726cc73ce9900e0fc6ec0a5b8af83f7cddc7d777a76f745a92755b06c4eaf8380f03b7508bc82f044e24e25e650f0b730da207388bd8c94b955aa474ed5e3e7ac1b4a7620064de500b98ea5614453d4850a63b70a4e2d3087701871b31e54f2d3e52af7dc53abe084fff5d11d0427c5ed43bee545582b43c0309912b7e0a125a64bc403475b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995644a93551ffa97ff539bc5fd3a73b8a7461ca566024e4959662f5ced7eb1f91f2b94c82056bfa2863108a7f203557cf65b6e96133eecf5bf45ce8f2b3e8836ce719dcf96683c2eef6772b6c41b56c4c431bd16d62a398a6b4a49d14c477c90ab0d5b7014362117e25ca26ead5e749ea667e70abe3763943a6cc6972a658969b041fd5035290c222e51776fc51739d4f16bc37fd44e8a15fb4b8b564d3f05b8f815bfef7604e34f2c627707762ff38f6145e1588b2c975148378145820e34b0985de8591e4e5975795b2041af1daf14d33db9bcfc6ea4ffce163298810d2fbb4d410a7d090b4535af5ed7056e0851b9c879dd0bb341706468763823670340b0f8548505652ce65100363c3b5c5097e4c942ff937173f06011125a82db07f6603f7e386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995644fd90957f03d3d0275549364e7d0f01019cb0e3b3fe6110f5103b51bb5a55659a18bd40473eb8229e9b1cd1035bfab31aebf5904a21ed36ed5d78a6360eeb97bf9950121ad20bc4eb8f235647724061dca6077313e2e04159772e35e1e9edf48a225d67ba0ed516b0f4d6e69229a34368259fa68b1f0827d3cdfea5418e81034b0526b3e36903a26ec6dbe072f8b606db544fb07829e705c414b987d909f672292783440170cc9052629ba7b55b8ef2de9b0b32f03cd02667b13e21589df5932aa78860eb04c026c79ebc1103e0ac93f90d9882fc04b5e6130c9195b10a2a107faedcc164a4c2c48a4c2fd0b9d25c542f8ce925d25c80d5dbe1b5a173d64b43906cb964239f3fb1ae72fb50f4488075d57eebc7c7e1c3d691d047d366a63ef4c386f742b28bdda6896475057a0342e5f1bf39738c5e5d001eabc22784c718e0b483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564d47e336a3d2c721602f0430b4cbcaa121865365d0513244ea637145e10a88876e6265749c84ff84528a6e17a59d1581688b35515c5d6f874d7697a2e8f07d751f9f31275944a427513d0280a9dd5cc66d788191319e8aa2fbb3c1248c138434899ed732e3bb0a91c1fca0d71f65822286469aa03f0cab420a4b2ac7c38213614a33f9760de7fba167ce04354397ca31fd17f4d2602ba4e166d9a9346c79a4937632e9d18dd9b846ac4d66f561465f57ac722e8015bce0631a1b1930f4c86fd1d301d64482ebc68443ec00e59916ed009a379937886939a2a3a9a037ac6658705263f4401a7bbb7523449787b1dd12a244bf2d361f9ec2578a4b1775459ec07674f37635b04a5b436f9d3dc156d80074365157b70c2e5961330b21e199e5e4068343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357e51d862e53786a27419138606db6e26a4aecb13e430ac578ef856604e43cbc570879a2176c197b4032615e1fee9d8f7334e4891bbd378837bce4b66b7650d2182e4196721259a05a17815232fd277232bac6c05527d5e1158fa7ae77bcb3fc3a4207816309011707c7273e561b6f64660a5ca9323fc277585271ca00fbbbb913b1eeda654f2a250ece18eb00ae152671c064ae7b7108ec396d958421683aa462dfdc7d7d435fa7668d0211610b6e9d2bc0320662ba951821977fea410cd38b60a663d826e50ec76fdb648b3a872eb92883946a61ccc2dc77e2df540bb4f44b4a6bd4884e8e63e86ebc858a07a254a77a14fe4b0d66b094733d6b6843ce8e2e110064de500b98ea5614453d4850a63b70a4e2d3087701871b31e54f2d3e52af7dc53abe084fff5d11d0427c5ed43bee545582b43c0309912b7e0a125a64bc403475b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564268ffb01e0607e6b875e74077f1bfc65fc43c30a9f366c5bf55e2f1b120b9b12dd76e932617f076633c696553bf4f7404725187a7126176f81da270ed403ad3de78a5e6b9b32331164e94239a4734e439c8bd307f7fb0b3396dda748a696c44511a3e150d82af6351408aa38ff00910865b53c106d3d5e3acd763040924c353c2097b8771c3ad83b7b340c01147141047fab9126625bfe50aaec3f2f3e7f020338b50f4fa8c4372de4bcb9346bf3586120a3a73ce3797f3aec123c5f3677427245677625f7b24934d4e20d3bf176d3729c20401937ea0c251358e626be8d4660c7dabc30383113111efe780d94c5d1168799fb2d3549f71538adf66fce1adf4b1a350875aad1295259b9ec455cbbe06feb79a613ed065d0a16779721979b60234c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3578a10487a03b2404ee95f08187c5be6457f7d2a065544256a2cc4213af8ca1a1ce2a9cf02d6a03e5fb738c03eeebc167766df683f6a5c8478ee316a3d4724112b9ab9216eef323772b0e5cd7862bd2f616f79b0631657a9536c0abf319b376975f10a441531cdbe1c1688da175326ff722f1696248f74696523e3293aa0d6007a950fcf197706b92f7082e715e5ee9f2c196c105592c05a4d22fb956f5082b60714c761689a574d50560c491c085f52565b27d74b79fd4475e244a20ff3d2ee135f8081175a72a679f1ceee492c03b6420fbc7b3afb356e473415883c06b4744ec5bed61bf9178411b95df3292d882d75874b377339bb983c2238b2273265513373752d6889912e3111d0607d5d86d447fc2cdc3a7cc3e80c3684d830ddeb14109d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956480fd7a2f5a22990648482b241c79ff18bfd186112422fa1df91c0b48f517816835f4c2447946711a20538e4d1bc93e3a2cb8e448d02ff47080f9d30ddfba3f690df2762b3fa8ca12fc850230a2785114cea87a22782ab103a1f0522d2e002f545c4ae01979998a5be4126b17af97a11f50304f782939bc34cae23d09a5817301915a565638eb890d25447e3480c68f4d3a0a054db8f237641d41b10dac5dfa019deefd7547633c27647b1a4e49c3307b368ea763944d890001d1d12b0cb7db003b40f0493c12fa3f04e3ab68b07a090571c9c9232bae05291fc73761e9cb9c4bc482445655edd56f40a95609d2c1d64a0ed5d140ea177377c3e55206399e12339654745457d56c6c42465e124ab92121606bf42560f8404f8096c8119b2b4b48343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357b62f197e13e78410bf19ec3bedf0c03843ed97477eef1b6160816862c9422266a04d4e7bcb96a14753a30e7df526ff61ba761663c2e09b525a22231c7f546c5072c8b9211a77e03b87222609d4e9d079acb9c82024646c7c0a69fe35c1ac8d551c07806e3786ca26f04a71600f2ac64483df26543cd2ff583a0f91564f52217db6b5f5363844aa6218a4bc4a58e4041e26a39d1441295e66d86918484c68dd6e389b7f40065725650aca8f05a574a92596c32f670bb400332b83936054321437927fc5604c648a138beb9166d745552a79ad142263034c1f85e9bc6644989833af06704015e5cb5edf56ee6cd226c45d12dce04bb3410057c632174e195f073e36c50b0590afaa39a7f6205049c8986c20504b2f07603146cb588e264f09ac19c7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35726c1cb02821a5a2ab1fd310d55d7ae4eead36a0d755d9b1dc2185b0679b4335043df7c389e90c8799a44513671862559584a5b5bcc42bc1ed0e31d5308b9ac5e6acab9547b0b8d791657be56fc474b4edcb9a473154e9c400745ac6c26ed2131d70104033a81cf05ae11595e448be866d112e37d2fd48424a18192770f7daa22c073130d695e2e6f197ff2417e543857714f5d48d359792bec72855941143524c4953814358ea43776f36349a096ce14b90c4a4b3b7fad542f872c538be50d0b06bf171c7dc4bc531e9a221f135c6c6ed4095e1cb0f352170de61e55082ad916189dec0dfe12390c28b54e0f0a7c781a533d9606b479c726b82626725654454d01249374a1da9f7cafd9840638324d5f67502439d2000d3ce64b0b284c4ff342c69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3574d091817ed4eb45aa72a1852a94b49117857f653256eff321115842400f9c93801f26d3faa78ae077465441af6ef93140d830c14220c3721b087053b9bd3f8393ddd6742eb454c0ab040f96ca6e63d51e85616051f961c1c594ba465599a931515f9a020add3c43446e32a012f2d990ec1efcb175c20a62bab293a43e9d9944bef54793e4e690a6993535f262d743716d67d9405974dcf1815fcfd36bbb57430ab80af60585c6a24868b701412717041b1697724e505dc5c00f49b0b8ae2c441533d192a03b01436667de167e6f5d572a278447a4a20cb3392f16324cab92c25be2ffc254e1c5d09e5b5f525e0feb86781f6505d2b0e270b9421221c90cba26a83241d1870631e19133b1b670f76724ead8a021ffeb5a956aa8fc25e0db76c350198ba3a66b0a6131254b570cbbc44359f47067ebc15520c5d8a3a791b5f7d772d2bf43d2b9ea43e2b0fc17ee8a514334cf6bc3db288bb7c198b246de8c33c1f67de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564db20cc5402df2713fdfaee0b8c750947c7dad439bbd0a87d481a2b6baa60f311823f1e7972178815397b143cdda8691fffdf5c3cd3b1d366607d427340f3df46828d3a78b60e6a62a3fe5b6f104af550c95ae65e44316d26bef0193dffc9000cca53744a243acd4bd4003158e7bfed2a3f3a2c175d618c782fd2182724f54005166eba5aa207ea2786eba71df8b4fc4e1451c75605094d51d4260d71539ab26d83d440091b6a8b61f5de450b5b82300e33bfbf564a171241fe7ada1710da1647b08f6e7c022ec632d2cb4578193a7e6693ee583a737da82f6c9fc776d0c6f129ec67c13cf86c2b6ff194654c93a06f59f8e1c04523eb1a084799e3221ac834784adfe06834c0fc78c36e5526f3fbcc254715544874b78a2a71aacd094718082ac69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35749811547a0711a71ca0d7225fb37db004038f60acd907604da7aeb5e16c5282375a97a21abe5850ff6efa560fcf540470431043e84be7951c44c3b5876d6244b04d4ad3e62bcbc57809c2b63cc08383ea0f0c45cce06bb71c808e053d2c64746e1c62827f12c3a26668a8c21f2c6961371aea3568502512f03db9055e0f2b01b58d4620a691be2213aa74d28da1f1b5a6fedc71e1107566d832ca0441133845db1c88a423d6138722a09421009741a5bd05c054c26b83372a71c1731f3752f514fd673398ca5ca5b3dd6e3326392923dc31d274d64d21f08702dfb0ccfcef079f70ab1548164547a57594134a88ded6455ca68630df0215d7d28127057a1ef3d0ac9d80ce709de6b9154a317d8d0b61fca5d372aea3a931261bf551d1743ea6dd47568213a89630c31aced59c348466f978c9b539c0e0a6855f57f47134cfb0a75b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564c2899d45ff12ef4c27161f42260220721d01815a622fb96fb1df3615794b5d04fbae095779f6653829ebe70d4c6d421669586c416f2fc05a11087f326ae3d95ae493ac5accf07130611d822c72a4374b3b40dc23d2d2ea7123bd816b995198748356e66de4839c3afd7254583b4c94089df54318dafc9c0d0427796d113b3e1299d834052ec20314e987de38080eee7349680d44ff85c61afe2a975fdb328b3789c0890dfc0bbe06392f0116a555470ea353304deb4f0c14fa36fc1456ad460490c3ef4efd475a0f186ee463b03c10758d389f7c559e627dfe10e45b8b2123569c24937286c38c3eed7e816319b99b017ac89e0cd4a7f47c39f00d3aadefcb354f37635b04a5b436f9d3dc156d80074365157b70c2e5961330b21e199e5e4068343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357a0cb2d6d7d6b930fe0891a2a2ce1ea052bfcbb64782ee70901d79f2f462fa7628eeb3d6b9fc98f33436f6e4998af617039e3a92c87a9267be4b826482439dd55f443514bf8407d54a2ba4326db42715e343adf555f7e9f61f76c042096145606eb782f42f8908331fbbf31659d08592af8ae4278a699531de319db053e3ca511defd435a6bb2e253f0f1c2390ff49227573e2325a3de0464618a104c9c2c415fb1c88a423d6138722a09421009741a5bd05c054c26b83372a71c1731f3752f514fd673398ca5ca5b3dd6e3326392923dc31d274d64d21f08702dfb0ccfcef079f70ab1548164547a57594134a88ded6455ca68630df0215d7d28127057a1ef3d0ac9d80ce709de6b9154a317d8d0b61fca5d372aea3a931261bf551d1743ea6dd47568213a89630c31aced59c348466f978c9b539c0e0a6855f57f47134cfb0a75b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e9956445b0c17d1b30f72f6a6a791aba3b9f3983b64152b98853707d9e3e45b2ccc445c58d2a00e01d1b7682e0ed146ae469052086ce332b362e6134138b5145ebee01ccdb91141dcf3033aed4944c80cb9142b624626f7960fb66b4f2ba244684a92206d0f65f8bc113322b708a12ca04d265534c2d6f2ee8693913dc3230dcaad63418247b4e8291cf19a7b384155d08e31a54e86051080afa54d40ab51840faee7ac388d21f79a39845ac1bc01840f6662ec0419e34bc2e394603779a21ed724c54386dbb4d46ff925b2a7ee36eb4e4f95d52c9d3226a43ca6b02dc08460de4287243a8c679224292161ce7fb5b782f150993ad2121ac67ad45c6c118276450fe6ef77dd474e65b2c3d45c2210e834c26682ee0a60806609e01609d1c68df383a412e5a7b2eee7e450f7062007d7ebb935b31d48b61d8e9801a9166a861ff553a0065b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e99564729593145a2af40d32b18b46b87c003ac850c91b69de1e1eb4176f19986728263aacdb5ea523677750213e7b5bd9e86de64cc165787a20629bfae142a274030868d8c25c7a2aa64baad2f27d1210805cffda5628fed9fc25b204cc07c738dd4fda8c177af718131109fb304ad7c15656cb19af7247d48c09f953f00a51f05646fe8a933fb1db0825dea0d876f6c9eb74c1c67335ef79e507cbcf78415703f703e7c28549d8be335d383b75588a24971109fa531d8f4a0278497ed46020def7206e536150ceb4d6569026337246cf2f0c7d13de55167b05730abed95990fcdc6c44b3df30bb6b972d52bfe95efa9e9c26ca5b5a63b8f9ea7082bc9a54ab90c54457da191628b8b9333b1c251ae332fe4468b71d0fbbdec631136225095ca68a30fd1bbb115fef2e799b81f22ba03d86222a34405eaaf683739d056807b9960b60374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357083fed62c337d70de63ad67aacfb4e37ff90291a3ba4f641fc9bf731b512d927bb113076e706615145a6c5686bf5535fc7bce800dea1b774de96354dcfdaff1793262c70906387675733c120686ea45cb182da1c9864816253a70f0c2d29480f25931d2f148ead008961552c39d04f1ddcc18a79143476767d0a050c5bf6dc1c166eba5aa207ea2786eba71df8b4fc4e1451c75605094d51d4260d71539ab26d83d440091b6a8b61f5de450b5b82300e33bfbf564a171241fe7ada1710da1647b08f6e7c022ec632d2cb4578193a7e6693ee583a737da82f6c9fc776d0c6f129ec67c13cf86c2b6ff194654c93a06f59f8e1c04523eb1a084799e3221ac834784adfe06834c0fc78c36e5526f3fbcc254715544874b78a2a71aacd094718082ac69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35743146075f79e647dd0895835ba65c0364a1a5b75169b7456f1abfb5e0d584a4fc00d59752839bb74d1511f0d348ebc2cf0f2f00817c4853c7ddb7c5828c3b03089b2871bf63456300f4c145d19f9ae28e0dd9a6714196d146ee87171427f52745a6e070eb81b53510e7c500b246c17569330de773552295ec7805f1392b426366514eb2353352d58b8867a23b04945278d6df572032b2b78063066079599d067e606567caa3122792eb6d450e57d202567e2395a84501e7954967220a88d0547740276480111a449ac8c846a986723481a444926598c0b42d79e780e089b0b09062712046c98710737ab6d4b0f01b26cfb73264d79ab0a5ec2d876245c8b8379cfa4f358c32d103eb855981b8c10443fe1aa6234d888af3dc5c61a04c3de5803cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35709b255395c77f40e31fa4b77571073173e66575154ac7c792bac603c7029b907eec9863c2aab7a14a4c2752d7ce5316c2a3fe11001c2ac3c8a6a910e6e73690abd5a37153987a7128a027e41a37a2769a962657d05d07f16aad387123cd84b599e90f272ee75f96ebc25b9520444be666d86224bfb65d407f65e3a4ddf71273d9985994761b1fd7c0bd36018ce25ff28cb3a720b15d8922386b9606050e68f6eba63931f31ba5930de146819f4a6c6552fc9990a996c514bd2a69a33d87ff263bccce8160a12190a1d9e5b54b0de2127062c6d27806c2c1c66bc4812839d5649c5bed61bf9178411b95df3292d882d75874b377339bb983c2238b2273265513373752d6889912e3111d0607d5d86d447fc2cdc3a7cc3e80c3684d830ddeb14109d3e445b192c1d2f197a47694dddc75921e70417547a101de669cc4638e76d5e483592221eb6dd3254be5a66a15ca534cf47dc396120e6687946175310c1ef105b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995649cf02f7ac1ad6960b32eff1d6a9f123963e44a6ab379b12e7332703a2854f856d0e25f1bebb3b86bab35070e337f813c2f6cc31504a4bd5baa93fe4bb782e57d56b6b86cb79497081ea262025b83be3d2a4cd317e4458222f3b09b27130caf3239ada75a8e46383bd30b932cc89879346de3800be19c5b47f784d24c22f1d1767e0db1309caec771ff815c56b97d4d0b901a1d1716b8a5663588a13ca6a3c91b82ea1f602af0165f9315e86fb404506d94532870aaab926690689760be5bbe16439940218c633569eff30c14bf1ec873fcf556375475ce6834cd016e4900ce26767adc57ad4c9a2bce39d57023a11d7dfade216fe5fca528f9573b312c1d0320cfa4f358c32d103eb855981b8c10443fe1aa6234d888af3dc5c61a04c3de5803cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3572535c5320cfed44d4acaf669b4a92151ed8ebc342d11ec6e2e0bfb5ed089471a2b8871305bb21459635bf855c35878368678350fa18e5434b03363523320b27953e46920ddf4bf5e0463922c86ae200acdadbd47c8b78d2bfd031f2fa0823346c0a4ec35c503070ba1f4da3862cf025c9f4c216d406ffa6f21a3804bb34d4222b15f2a208876f344bc91f708667e396145d9dc23fda23321723dc23abd34d81d8788b26069e3b254103d174bd95ac32dce7a1b7152fdae3555d5647d2867fb6990c3ef4efd475a0f186ee463b03c10758d389f7c559e627dfe10e45b8b2123569c24937286c38c3eed7e816319b99b017ac89e0cd4a7f47c39f00d3aadefcb354f37635b04a5b436f9d3dc156d80074365157b70c2e5961330b21e199e5e4068343d25625483c238af79256948d3a67c151e9c45179b08302bb9b051b9663e7ee5eb9c70e1df8c35185ee87cf1b3b7717ca9e84582f6c47ee4018318e45c7d3b8d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3572c91132443a84a20929d611c7aa712098dd60a39fe802a1d5ce8a846fa5f4814c7e37739e8a215755e9d032469c7fc489fe7b706426eb05f66ab3d40be0e9b2b89518f531e30fd30b1a4db4767d9e01dc957a34ffd7577598d689c0998145808f436cc01faf112001d016b21cb46ff357981932792837e6610496474faa52e3af4dfb70dd729ea259650f92c12a5f136de24b25b95b85e7bb1485859ca942817cebd6e329d500a658ed2956dda93132137781f0cb50cef1cd6bf5e07a7732f0a3c353f73daca90381655cc5b9e53714a5a3bae2db3258c0a0a810e12cc32484b9cc13f148d72c01295c4117bdc3c5c54a9df474ee6bfc9026bd9482daef255219f5f0e4cc792265111c5d152eee0ad6972fc8c2df5dc84568afb7831bffc372c2b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35712457f7a32f280670d971878c06c227a04dfab1f9cd203121898134e5e5c6764055b4610390ebe1c753018786824ad735ca01a35ce879c5a6d88c1694fd7a24062bd947874e53f37637bbb586e18924257a15b7b442d6c30e4a16b721de04a5dee71720240375b72c80f282caf4ef638d07259462d83f13110c6585df5a2da58803c5633964ad838a5aa5f28442d6729abb81c26d1b0337bd09a5255ce0a9843b76ec60304b9b77a9e6d6b4fde041862385da9605928ca00361b6f5d70fc7d6a74a7d43631ddb3224459c63cd2992b11f046e840828e281435d64408c8e0be72c7dabc30383113111efe780d94c5d1168799fb2d3549f71538adf66fce1adf4b1a350875aad1295259b9ec455cbbe06feb79a613ed065d0a16779721979b60234c5f082fd4070954e22f2a4faf7e4a20a1d2240071c685574fede75d61dfbc379d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b357d75bfa199f090f41dc5c2954994f9a720b9bd16460b6b3325f56954492cd0f6d8710e0655319a039272fa92f28afb86b03eda964f8f88716d1c1870cc09ff97c24b0b26854c6a7516f66b61a36a054771bc7187ea15ecf1a06debf321ccdf515be91ef05f18c4d1dd3035f78d854f44eeb242579abf3343c3e22337b480a133d9938b00ef17eb959c2462b2b50402e49917e280665b85c3f02163e08fdba8f6a53599f24d9a4a267ff149f2467ff3d7e802197557b60eb278423157b041c8376e23b4f6031efec6269aa224c6013654fdd74133daa02471adf267211c96dfb42ad81527ee1b2287ceae9c60e8d98677d89fddd47916a77682cac2e6e05261a1d3cc4c07477e3803f1dbe0a740b1ba053c1d9a377345c656c1fe08f14f2e9a81bc7ffc358ecdee14ca4a4643505825734829800358f882f453ff9be6cba7e8a00e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3574bdc2a71cba06200de56b20a6dbf5026b964575d36a31c6326ccc03db7630a575a39f501489b096910af9e3c235e0b5e7194051c6531c865ccef167302c7e16c37759b58ef242e166b528a21b9921b7c6f9e3426fd6baf36ba72790e0d46d90f022b08536d2f9b469c3282169890cf6564608d75106fc535858dcd769f48ab46ff01ea1b02c3386bf2ea3866b2031717208b6d32bde6e95ca429104b77e9c77091317010f431902284f60008c5bf38251405a2148c26cb2961fd1965edb53816fdb68a078f23570aba26d15970d5c370c9121a2228a2370e41fcb8695da58d68062712046c98710737ab6d4b0f01b26cfb73264d79ab0a5ec2d876245c8b8379cfa4f358c32d103eb855981b8c10443fe1aa6234d888af3dc5c61a04c3de5803cf03f25515c71a582110ef536d9f9d068589fe1ad1003a772a80460feef4463a374fd3313a2e6400accb8e3842217567f0feeb38562b0878a22ad938fc36dd7d7c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b3577500580f5ec9252e7385cd4981655e62824e1f64dacf8a2a85e1d7215c2f1b184f7bad02b0beb41f2e57b86cbab6781112baed53e76d6f1f32c8fc7ad99a7228f694c969a231b81e8d54a56296688f66ebb460033898e93a8dd61b6afa792e5f7f863f308316a877f4678d03b82db86e4d9ad7590640dc40bddbc06ed70a073b9804922901c1c35aba353750878ef93db5bea33d883be87d2ae62a10911ca43daee44d49d1763772b7d311463ff0754ee13ba878fefd7b4d992e1d5b01766c1da77ff553c54f85104cdccb645ee9175fbc5625373eb27577541b314316018a649cc13f148d72c01295c4117bdc3c5c54a9df474ee6bfc9026bd9482daef255219f5f0e4cc792265111c5d152eee0ad6972fc8c2df5dc84568afb7831bffc372c2b5325676b09f132b183103ec225e54ac025246299a83f6274cc45699d0153009d28ee0a0a772606eb49415c045ad437335f7a72559016772d363212eac8a3258d35f9673a703f0ccb8b9566da512d002a07fc0ac6d0355ea7c56b27d71cd71086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35787f1245126d30a38928d8e2e92f97222ad905c53222ba00459f259647e5cf3310e39ac1b60574275969ea932c459fc0ef9c5fc0633b50655cf77e77b10d6cb5aad13363d7394e246ca91867658f9434bcaf2cb2a9c66a248a4aaa0090dc1fd4c5b4d901383547715698a3e634e8cc84c64e3e24fb86585629ef74836cb726103ff07e54e05cb00594efa3f79bf6a7f32db25826665877938be97400ecfd2d40c87da8a763553535bca92046c292c2a3c40999d224bbad620c95a8243ded4852f00604c74a2c58152e9652f7dda06d07691ab7239036d1137b5d9a127e895150ba294fe49e9fa6d2a42013b744fd26a3d14e149348604c14898587374a7ad4d14fd71ef24c0a6500b892e1f69048f1327abe903296c159966b38ae45deff8be67bff99c793a7f340dc1991a061c14bf767cb6f22ca9ef3c64e138505fe4932b5d65b1f775b031d7436b2c0d791bfd9c07ee8ff12a25af05165599bf2c9af423245b3e504ad5fba117bdf4956bd28da01a93fc9e66c9412c2d5401b3048e61072e4572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995641293d7331c83ff3e750284400ad75d5949bc7a63e180721e4617362f2e916478b4b97f740b026b230a3e8d3c46f7fa4cac8bf37edbc56b28f15a3c2b9f528615409e25632966a824274ce165d95735709b31dd649d6b6e4d165ef96956c9755fa0b73f782f8b5a656d4ee55f9293df4320feca0e2f97ea3f91a9927b4f3d2f424a963b15513e2045a89a6903460ba70c7ffdb836a26a0207c9d66e738e1b7e66372a80362427a3749cc6d554e2b354666f1a7a0f60e1530bfb991719ad44dd3be7e03311d393dd214f5c06535e0fe84ce9ef18037f6f6d79f308da13f974962f84b30d692cab0e4888b6f35530ccdc30b4e30718a2050d0809c04325fb2f5f7a9289ac23ce6a07644ffaec38d5e84413f01b7b52893fc05984a5fd5b21282524c53abe084fff5d11d0427c5ed43bee545582b43c0309912b7e0a125a64bc403475b3317b770fcd6f7733a95217cc460c84663b53df7dff5ce6915b3484ffea4467de4348e693bb5a81369108a028844d46634613773c974691fae83531f933494572523ab64bda1b6bbf996564969529c39c60667d8e575e453ec126f5e995645f305c0abf0faa5e1918b86e4d05b95846eeb33da6a9fa0cbd740b567bd1b37490a41468c511f27b9a4642362c1f4e58ac8b270648ebd903f356e81093d42121b458a1428287740c01d71f6cc7c17c6875bc9d0d6dbf851f7dfeb33bf26907219cc65427af85975c4dca12109a1ab40b335d5f63a2088a4eecfbb4117cf09d712063cb5f14197d3321efa316861e0f0c13eee757266fa2254fc0a30a26574a5f30a18807ece1342ed288fd3adf8c3b525952e249e5bd9a1792ee127b2d8fe61f06bf171c7dc4bc531e9a221f135c6c6ed4095e1cb0f352170de61e55082ad916189dec0dfe12390c28b54e0f0a7c781a533d9606b479c726b82626725654454d01249374a1da9f7cafd9840638324d5f67502439d2000d3ce64b0b284c4ff342c69df0578241d94f830795576e17712c8b5c8b687ebf43436920f318bf668475e523607b9bf7ed5cfb432b55f7f52071c5519c5380d864119274173ed82363617c47405ffaf2f950fd200d7ce6e5523ee0565d7cddcb1e389a516b08a28b753086f85e4e1a97c44c7f252a2b6016e04cefc5605dcb1f632ae4686e63da41b35751f0c74e4985bb2b896b3a349234a00e8634493d3a1fe925ab45314e8ec0994a198ea81b2b63261ca5d359472b0353113dcf4a61ddfa01066343312c2f45dd2876d9921b2d03f3018e94bf2268268c5f7070281f3656302d1563f06b7136902f82b7b3678e89fe2a990d175e41d275627063b706e7e5be2e7e2ac8434eac833caaba2d1d78718e69181c3e03791e34547196141e08af824c2b73aa143fa7ef597616ee7be986be47feb321578c3b5e4ccb8ff66140299519a46cf17b80e5a915ad6c25319b1fb502354e5a5d477ef37d9023ae20ae7cf638bf6e8c681a08134c9f85c97a8088200c26671343a656370a00ae0571b2ed352bb0f70579ab5a9c7ec7ba4a411da8ef7d59b72648633ffc2be9c00054f8609963e7af2416c9500164ab6e766210e2581d93887f40c68f0a3ccc558a6aecd55c48b2ffff0cbf68f66c4bb87e11d5c21f612070950121b99975eefcd702948fbe00e36d770bbc553c02d27d3b2829953915e60b971a33498079c269a818b655c2743fa732693b5c12282d3e65036f3cc522b35bf87b0e085d01dc78105804c7f15622f85b6848c52c3139edd908e589d36469e8c933a573ad35cae4662f9191050b2e55f95ee48b2517c1498a52a867b66a23f71421897e452f40bd1c32b5b3b860905f1050a3594b77c98f594e3b7b3631a901ab526878912e01ae2919b0f46c1a20680b47ffc5cb53c366cc7559957b4adddd43171338bc443ee29154ff61f9531dca780c46b59c682d2ed263fdc07455c61925048d0c3938f7f5562689a5d91df3ab470e1fa06c3a16897e601828cb56cb392e573dd4e25db2aa7e652d306050c35f335cf9931c744c90d61763afa26b1cd997667517e012bc59e25779fd706d6fb0b6326dae6a1c0800252f2bd7d549a8a892624d836d2998d03f7615e6630486e7f03edab7a62d49279660a2a2874f3cec2c4bb6979903b5babd763e388b2d0f84745b1312411d26f5972c37921157a60bbd51b0ed994b61e7d1706f578303aefd085de2fedd67b21a861ce8ca0a38796f5a6ac20b3024fba62d2d21dd1d54b552ff484e452f1b19e68214e9299943c8f68945d9d53b5a7bee6410ac2eed71839f5a397981e76246fc98691dbcdc23d99fbe0f6131d067400bbf583ddd96486d58c708623ec202a370923795ebda372bb917446614be5a3d8779600d6f277bbaec6b7d7f3799468eeb8c7e86c02c10cffbb34c82f24f0f6d170722901e031f75c9f55704b63d217921d827269ec9686535cc630c10b76c81f0bd2ea639645bd4b2d93752755c21bbab3f35ab63a7297dc59230bf792062ca5c007094dee200669b58252e1ca8199d82da3114098b5366e29737093c454d2d6d1c7b8aaf9b0b8a3915749b5a8b3834113e202461945ff658ee09308c3f2ad762606c046e3358720d207b306bd46dc5a1d561102cb122cd696d12ca922312ca839b7e62ee736093243b1fb3d75d36d801d61aad600a644b756649612659249678b5643921fa242eb21c219150797b945bd06666436e3566aa651c807e8f208760db0d3c2da04953cbd16fa714fc6a8785bd3a158f75071efbd950fc0f143a853a8408d4175f0d0fa3fe519a98547e70000d2d7337e649effa807c7fad9b53a0b76b7abb0c7d5f9d96df4743c60b4bddfac5436bb4d651f7fb453758a85318349d8460ead03b6a5a6f7b5d63f19063b8f6c92e6a217d50f607342705d2d246d6caba51dea0370953da5963413123590e7b220fcbdf14237b57b91b07c471478ad4e740fe7b5c7a0b9c7f41ce8771674c99507817225b07e599d41fdc051375c7f4776f137dcb2d9c927d0e5a5bee14b7a11157a69b061d9616717b8dbaa177e7f2e40393bc59430d71ad4def50552bddbfc03e2783b331ae8ae471df2a0a6d0fcacf7d94eddc02edd34071bf31577a7c4af4035b27353a8cc07226eb4fae5babe139186d6f672ce3e9a96f356e363341bb186931a738447164b75e00c42a5f27915811cd80855e8762e3614bfec9496bfccd12e1a8086455b4511a9db57533931e766f1582c56038132e0dc664706b9bd1c00755727670687019086805a10a55bb730c17971b5cb343731898490d694933c001c590da638dc5fa2efe32076f9058650d4256b07034cbb8342e5c7e3d95ebbc1826c22413fab6912fd90b322d89bfed11efb7d35039218e135bd876748fdf960b268b525fa0f5493206ed854b6b2e5650eaafde6431baa2361ae7516044b40e47a4a14678855bf5756043943c0b57b1163989a82818c0aa6ac207875758c0e752a8cfc53db3c816184cf4097360e000690ddb15138ff31d31a908651dbfb8573f77fb245a9e9da4723d2ec610c8d7b029daed310ca532de1a97aef40d364f7d7e2186ca3e41692d4dfd96777b1eb9f408e951687d78359526895b7a43552e8a1b611b3d5085d175290a483d20b8bbd9247e177e23d6ddf85b8fdc2a250fc59d15bf9206340fafe11f513d33216c43e26f6d8eac69433e4b4b2821673664708444ef74d12ce1d5881dae13a30354866b5ce921de7ecd9b9b584e38616a1302f921dd14c923055a697da4062e57c49bb214f9a689547f9ed809d665914e23842a203927d2573e512c737220d640ab51dd2f7282832efac00265898c5413a77b991683178000040802224057aa01a7650067a7c3c179bc98c00032b7b44e5a21a228a6fedc6d74a62d3f0e7b0c0b1d60fb3683ed411284377b409bdd4843c6f5736636d6b379aa1d625a52f24e3c9f38225558c4035f40974d1659e9c34c22fc3b63479a8c0681b121270be903374211a93bfb8c243d7d7f4c2ee1793802e3be4d6c60e98057b1028824b92bb832e0ec365863452b638cdeb74b7e41241c4060d9286f32d027bcde152f030b5e23572cc61e0a327f743667e60c23a13b0c3b45f3421e52c93052e8a9327b7b4869e4294e0501c5c83e62da6366f92a684d8c3b1600b9fabc5ebfc98c6e5cc7ed378ba71f6f7911cc547cf82e1c98d6bf46a5561841f65c4f4fde982a3ed71f17669999930380dc907cb12c2e456dc18e4478300854e4993036b4c4007b6428de0cf402517c0dfbad64c0bd4e2f48ca851e1eddec0a3ea6cb24327b020c5818ab57fade0e35713683464fdd3d3dd0c3440b24a27f476cb6aa42bc77d224d497794faccfbc4060e4886d615f9a48641f54256a659e685cc52c1ef162e3360a8dd7162934c22d2efa0c5d6c33140c7e679b48c913e65f91965a00c9aeed7ab7bf774f6221993922c8ef6115fe4f235614e553b9e09d40829e0f7b969f8d4d2816f437a09eb972fdb8ef4146be866b562dda0e19dcba65b1d8df029d4ef07a9fa4770cf53da846de48a4574ed7c8439baf04784ec19b2a2e90420e6c93aa2b4136d22cb53c2909dfed7124ec4c356afcc6d6513db67449e470a758477c730f96c28316df5fda247532243bdb2ee87e5995947475e05821d21fe300672a5d7be948741f8623e679329ca07419a2681e7df04b2906a5c30d073f847eccb703620a464461b78c984e1f943a2d40fad235cdd64a3cef83bc533bcb5a6224e3867bed0958171c7c482a45e254447db30d3fe2fab63fe4245376821a0d5da65bb501c5714330a89dc845b595c107da8c7530410c45590bf69b676da5102f75c1965b91198e6893c4955898e1e22b3a0e4823263b7725aa853c638ab11c3e1b4f34125646f42671706530e2853a4620ace51591f5ce7ade3e9b65ca606b60d9778859ec7fb845a6fb9c21b7bc85701c2a2c1d74233305cf1ae926e2801c3f594b027af6adb15ae8964d310d7a6b78406e14489a525d55d5c2a64960b2c56876c08042a3ae1036b6ae41486906fd5667f1a04b98821d6bd0053c3ca92a60371da2503fb65c4f1bdd4e6a7afa1d705489d49f40bb268834bc9fae2fccdb127bec805c753f3d753355f82655da9a423858d0ec479a82100882d2e5126402df4d06882b31df9cb41d13231e437b35b5717665fc281ff9ad4038b83735b74eb53de326a1711270126895f78f6426a85977ad5ef4668de10c1e82c4b03bf198556d032fe357b2b1085db9bd0f049d0ad5139c99ee3c0adfad54cc8c7e4b335c6a244d5eba6626092f6075bcb41d41630e46fc9fa87b41fa7a604d164a73e7d1d80edfb3487a7d22060f4c47316b1464f350dd24141b7751252010bf725957ee9d460307e928e7d4cb7ece3a3e6b4d125665a4fd862ae185141892dfc048fa0862698e8c7714fcfacc3b80853d459fae4074dc617a2d36c368511dd8d918e5861e5d21cca2202c37015198bcaf0c46ab0a6435e8994ca21d053a31c17404cc5f63351f4efe2c6816026e982b6866eba8410ec1d5a17c8900934cc84a9a05bd1d1b2adab3b7722fcf2a3798e51e2bf9fb73695d0ead23facfeb5327f2e73d7b62844188584804547be83fe2157676f69f3001c5d8270bfbd2463747261679f9f233513601bf63626cf55a3b3e2060522f31625d101d0ebf41d2018b365d0ae2cb016846a378494961582be42ee25fbe31a829a47d3501dae3da64278bb343011cfa51505128460e824e462b3f616bf5a7e84d9d6682725caf4645e38d6957592c11240873622f1f3d5f596d44b82b09e4684aa79d3072e27609174ba5f50a2da1021b7bbacb398af6623eed91640a71e8ce2afad1bb0ffff04f3b30e20e0a749e461ce028f43eeb6ec4796d124d07cb78cb2373eb1f2bae6d1c129889d8285d4905613fbd0337613df054889d1663794218172b97cf0b1427fc41ced8843d4dfc82179801dd3f2c9c74625041576051696d73ca2fa20d1f100b438cc50e577fd46b3b4109511739207b42217af348c5fcd2017e98ac373769cf2e6925c762ee5b45305bfb9b314dafd85f89c7012d8332c92b32177c5fc1cc7750f42b19603fff493d01919279ac312960cf6e100cbc95ef310071c51e9155c91836e42e4a2351c729a2548f1883471901129ae256b05f974691d8a97743554a27ed9bc57b923a0b34dd7ccd2cf1ed2940bc66ce65b0f9b47cfb30ff43800801221e69b76b4f36f36155f2d32c9543022247033233d262717af3d60b2f45f98e1dbc0a795177950848e318e73c2e98cf363db83b76b5190e1b3216403f3051173c6c4a51119ebf9149eb64087568f7a826702a490805a00a55aa6738503f6c9055e5e80717798add20a290b35d65e3047836713568e766a964e6c40e6b8c15bd052fd729452077c92945a675783f3641134595211f9b903e348a529739dbfc5f09f697f36d04e4281918dfec1b46df034bc6b6a45bf2ffe33d1b6c70760254f2192efa230f611870251136484b8f4c5032359dea6bbc6e551dbb8b2c25199c7a7be736c86b3ecf1626d573e90e707fa3041e51892fda16634e21bf255e0d259d071e05244e1168d307bcd23e5185045573abca173f291427326effb6618117f04aa598b42bb358c63d99f99230bd72be6ad44c0750e8f6d27629ef874cac1e6957c7b0853769c9bc29016d127b46bdc67c4da5d90663f6d603611e1649900c600778693e5fd61baa65d3435a5efedcca478e42f0495db4c15d9ee2026e3292b80f1c07163964c9b945eece1d6970c84f73587ced49a3dc886f2bc8ce6b8dd0c026ab3d9172c82a306fc73867475b67a73637352c04edcf7428089f7017a3cbec3072378b0a62ac304efb4b93021671733be28fd44f63580679c3e1f55a8b81ce20089a745347134616f48171638234b64c75d24416413353252e8da361f30a6c5562263e235d301637599da40bb09de931ecb9571ccc9869178116054b1cfd9d6cf60f812ad204f940abfabf4fb178970381c44246042434684fac86603b44b24ec5c8a3051e79e862535a23240802985d2b65695efef47e75a423ec2c7f08804c2f8e541acf93c61fe953a80006e9ba312cfa7256a23a9843a1272b64af196252b44dac2daa1ba315542d36111175e06da9e16f3081d9e8574b51f846b8d8f646745c2246438a671360403806c6145a398cd5ab177419a7508fe3b7062032a73ab7d7ef5cb76555019589fa4d93f69b0bb674f83bf60acf05c563a45bc45c4931a53d89263773af48c499c664197eac385c9f110be60e7d27ce26a27996864f34abe0535f2dd8e26170bfbd5129764e4e8b8b977e202b3e6e5149f702dbeee217581efb6d1b6ccf320d071a664cdf420293f2d97269d68b4f6b63bc338fd32e010db88445e08121106b1a6f12545252357199d2637833d17c2c953d4dac30045d6aa2d35181d91e70a86db1055311de7d7c79dd03254c7803e0346d4e4554ea779dbded5f54681d281f88091b6383d151bdd813649c907651c12605214c24fd22596bcd7e90f501427dc5855e5e8499755226df358662206eb2b14221e2f8e3392f725067194b2b61d98336150bae7324a699bc7e16021c2c236267680f5d193d88e5274019f6865bb46bd715fb98c87c82ffab4d24a4b83adbb42f565d5cc719a04e3e14dd6d1b4e61094835e63c8b2880487c0f8a68da071c4b9237ec83f119c6e30a28943ed47eb320bc120935845929088570589bb941e60b335d03805f2232a47d582062273b4592f4033f868632a2307c67395d4b420ae6e0242b5e164df4d2a25c0ff2f274373f3970e9b99041f09bf4519b136e30befd422425b0fa4786b019794700b55a8e60fd2053e9984e8d913e1a56946143aefd4f5ce910cf3cba741d033cbadb46a6b6ae0caeb1a34e875da5770e2f1269d29fc6629b909d0754c48531de37146ef3202e54ec9c3a1dd32d6901c1c66c76550cc61f8154460ec64e4067b7345e6853c059331039bf5116cc941028c8bc3e7b528f7a2fe2a704e6b5391d7bcae35f0af55b5aa9601653d6eed504837d4a620826320cdae09f7c21ce11305938bd6f67e13f6e38c1f67d11ed3e2a545a473945b61523405e0d6616258f3656f62873783e6a39a293a34e4e807b15a4cfb72cf2df963039fcb2779c45d42f273992099ab03967dca4e07a10d9825169e3fb351983f45f0957cf770127700666eb4b2d53b5fc08d5b4c6507ec16e4aa390bb391e69424b08289615371812021293360627973b0c95220b59d377440454fe0a390e43bc390c19ef2de21b1b48357de516e013ea056733c007f5605d66d68fb366dac36b30fb6f5a55ecae110d0f5a5c5fbdac7248edba574075e80e36d2b576673d55811d2a1ef86836595e4f2160504b4a804823bb79b311f30344165693100577ff09792e792b5b1c386d76ba908505e7ab9d49cd41a41298bbd2491756116b72d9c60cadc09809f720de1afe49911a4b597f1a21501431b928cc033f0890323a677727dae68c3292bdf14953fe6a6dadff453f6268c36578d06e034d544119ec6573695db7714edfc25027986f311228d305365a69f42b715d395206d4a86891e4672240cff171952a64612d00c565e6118e46a2c64f42975dd84c6b646a347e65bf02a4f80d4620a13479c102ca4b70fe1774fa3d38456ffcae1bc7cc7b30fbf5572d37812251a2918a251fc46e71d33b12180e1c1e4e4e785f13c3036b25e2bf4f01d1d6e50bd3fd0e2282dffe1ca3195c42c4ef8a4e9d2ca22e5e1e203846d74b72ff3a53656bcc421c3b681454a77da142ab005211db6cd720b0db43677667fd311be8e241627eab1d3dd5776cd5dfaf1ec4a55e6a08cc5c7a7c7129555e65da51f05a425597115a346a43ea612a9cae767ef0196cc2898a1434a06a349f7cfe10335ebb366785f21dd1c57c36850ffa745faad779dc948c62a56f13404c649c4cedb439630aeeb1277b100247d733ea79d1f6e5261ce9041865e76c4adcb02662b81a9a5c5b6cd03a01932947bc54fb22de46bb43ba871b39485c0d018eb4557c6045f658d5023d569669c9567655fe6b134bca1b3c47b163c7018922928b137efe06482ef0b4d90e06910a0b012d114e7da62031b8855e77ee940d004177714bc52a1d65f96763407aeb0d653b5e0b7949e9841d205fa86aa6886f0884a92f3415f8780ee00fc92f3629d00e33613c70ca512b227ed7432d8d520c281fcc1012d0cd3905f7241948ac8f0b2b6eefdb1421d5a72581d7dc7cc9081276df08b35215758c4387e1454cae19754c08ddcd002737ac53b3199e02b608a354393c5b2dca617a5115854c7b593b602c132bfb5667fa0d131d875e77d592687099871c6b8cec3023c6961734649150191ddb4e5bc24768313c13a909915a6c79f1abcb34410b0b182e62be16cf5e2737cc67404cac0013302ecd142d37defb60dc5d247438cb0b01fe24bd786453477bb980311f7c882570ec6d112612111309b206547268161e7b5497c13d73e1583aecaf8d4e67a5b64033f6af1cbba24365279baa2380d0940033892246ec65e5334fdf9f723ed4c21997a3307900a8400d9242be076892aa2f1ca6e66da05eac3ce0e6251c8d76e43a55295071bdc8413ec83f883275e4eb1f9e58f234e4906b75594ad96ad42c400bbdc08a69b5368312aa32596326cc8875346ed571eed45028c005ef0ad38ec617e728c8094431af56b7e9c8674da9107c4b04502da340522456b0837e30d5ab7a652b981fc2a3895d335b3b7088043610bcc87c5dcc328a37195a9e11f90360651084b929640faa015b64c107ce35b330a4d83579863e8d09bd3a3427ae4a3c46cbb3a7543d94f06fbccddc36df746b3b4111ba344068a6246114a5662baf581ec05b0d55bb95f33402d93b1e6f54211bc02f1d3a29f0f22089cd8a1e4f2c445b507577354325ec69d07d417e052ecd5cbde8e122e2fb8a045ce79d783ddf9b6e7c7be27af5a6395cc4fafa6073a01806a7c36e0287323459d04de6716fd7e47279d8f43775c22c6c76a8cc36e6c18825769e687abeaac0272dd0b64bd3ff322833751150fc40483c52f9e71a91c5ce32fed90340f2ca956d47f7bd2595db8b13ccadfe2ac76e4b6ffb1256640064fb7bc44f1b11c169f7121444a83d1665b35d830bad57a2fe773434b05936840334264b9cb5601ec96f589a04965d3ddabe5d07fbf62f0ced654ad226fd72e03ca118e8224f57452768079360b70feeed4e637d1c721930dbd46ebb5b960593e730167d36c8756d9a04554bb21422cdd90757f15f954b6824040e20d07f0ef05a031611046f3b72109106e077d070cb4b297a40e67630e0e8c0624ee18d4c469add33af88f432d137247586e70909bd410e5f8fb051596cc490218f2af1665926f146ccb60e0f6febd80b4b7b925f7f73ef112390d66e7cc9c5296208b31602f0176fa9c9ff198716c047dcc9f77c6984373d5182f71c0ed40b5630f853133dc3736953f4c24a79e54d7d52c7ec587d9bcc2d4fb7b7766ff9342b376d65396b0d8f6d139b8571c1b56e5743d59f02147c935140772403c34ceb70b400f14e6fb9426cd849c05b72f5982333eadd664f825b63b188c84eb9bbf77687dbba1ad2ceaa75cf65934b16cf9d45119a52443387c2471e63694cb780af3f89aa372cebdb013486565f40a3b44a0c500c2513d9ec236922528708ad7659215f5d2f6d3618e2422be7be3aefd02b3be5e4364dad4d8c2bc866ac61b08c0b113bb508356a9b324184f941594aeed2323a8f6f775242967a1dcdef44707b4a6f3c79bf79da87ec4bc723984d9af9e52a99f55d0f186f7d6142e84340373bc10dd3c4cb7857799e050ccc3d0f446fa94ea4d3be504825de02cf0a3b39a4f198272337aa4cbcc7c226407f76460555a919bfe1195161cc3c6b3555d10ebaadbd1c43209a14cb0c6a6d0310ee65a1084d50e0814d542931e70ff867e5283636b82ee2d2426e0a5da9670937f57c9a0ae21ec1c0510eb97cc83bc9888a1a77038e07080d8178aefdf749b71c4052f817453aca9b283b2fe5ab6651f51a60ddc0840bd4767c7a45e09e264cb53120cc7c784aeb5b8a4ea1739854722e7b424cb27a4f31200c498f490a225ebd20587fb54b08d17ac213765029462fe95a006cdf2a014dc7050472e402228ccdaf2d7ac18e7e196b953479362b7a132dd768a7737a2f97463129fd9f994f0cad5e7e0567353718acb011300fb3531d032b4f3c6302288fb829332c840b4ceb46cd71288369042f327d0819e83445bcbdbe21e48e4347d35d0a5486e54258b504d23103bc206ae412e90c18045569603f1b52f1540c27d01dc27003063b2759934a1268441d2e75c9751fd96e635f41320d13a8de866f4880b76f4ddcdd1407b25c68cc5f3c7c3ebb5d0c436bf039161de11d4551dc5371763c7e27c58a53d4734158672f8f5ebf7e0749a5956841fbb4c24794e8c65965543c1fd687841bd608b7100be85a2745ee367e4958f5713c2e603e5238e9314a7df04783d3ad67c57a2a2707d13431a1b9d35b81388a17bfe4b6744799965f37640e01d86f3b6c821c49381904266cc5ec7232d0ffb2057e5dcf308c7fff6a84f06946da4585691e9ec3595e86966dfbc9530674d8ca31db71412d4b55c65e5c04cc491d37535ae864807dbdefc7410225cf5c6954d9435f4df54c14099444bfcabc43314dc6044494a201756f917b2d29597c311d5f301366350bfa6d2404d7ccb60ac6601f2f80e878381e01d00ef44c9d25d01113176e15bf4697be3842012a9c1f08d0f92f6ce3ae49efee7159fbfdf90df0923b374b2d402cb208770f25cfca49eb78e920e1c4c103f83ee37293c7625792f72069de28df2670474d5e239fb5383617160cf684a1088da7d17a93018a13714409327f8ccd7949ae1d47b4a88721f8db3375f6ec8c196804071ca2e37b50d9413e089d0441770c65ea38f4dbc00c93511c2f9af265183d767b780a7531208fec3a4b47fbaa5a3bbe2a17b5441e4de303701c67ba0c03d5765a0ff995cf2e3c856b3e7b164547ffe7683056165d176c2d3b0384258656926e5f5b5c3cb069d811f4526608a73400af36399b7933730354014f1cdcbe4c3ef70d69710ad86b6a16c647b8eed91be51b272f4c1b4226d5e61136fc5d71770f9ad779e3e2021239ef8e6d6107f022589ab7343075ab7dcaa6c223d636b15644014e7676c81b4c1bda1240c60364114c6e690b36ada810a767b43a146b811b9e65814485a46f769bcea85c6957c5101c9d9576497c3c0d466e330d35c1d471a9bda10174b2022236c5b70013bdbf2ee6188912917c39218104fe1d7394f66078d2d764c2486860fa397c62c21a9f3b49ed11155f98f4087b66b5708497f05f9230956d37cedf7ba385ed4142e50d5b3d556d32c85cff1eacfb9f2c29fda42656ba664ff1aa841ddcc3c91b7900336bfd8af32ce39a2d1666bd09147ab5c40dcdcd2125745fe77b4873a67912c20428b1642462b3b8974c9976954aec207b599c131d3c4c0e3d391e8d62213e78360b400e313ab184900b5b71345c22871e001ea79d2a14fb271a7b6c6a15e0178d1f0c2a16411a914f55824b3966491c7827faa92c19832d595547a6065582195e104af1ee1e6dd0de287f01fd595079043fed9d8c0993223a185b5e8465d28b121e5bb553433a49ea0a41783b12a6acf147496f564077d1b40a68b5bd03be96685387a61864e0678c1edeab44304c7b53794a35ce2fff80097d4f667b236199351530a696323c3fc678abad1f598d9a1f44dff1001cc7ebb20fef312527dcf731157cc641682c11c92029d50671b039382a1a3efb2256e9ac2af9df260c3e3db80b38eff968cfb00719e65bcd1e7d10b53558a04d45424f1d3c86ffb43218e131359d286e7759de2975eea0761cd0b1b800cc7f3225aa234a657889712dac218b773115b20adf351930eae7042cf0983a08119eba7dbd65cb7e3e18da60b3208c240ee5d525798332051ce360792dc35f27722cf5178a68fb79daea8b5e3bee052fbab59f05687e8f3ede84c7535a3ad848ff0f581744bed926b208c41f316222585b01b84004d65f3c5e522e201b323e727ded2c3c41934e7786ba2228b7772b7d56c5b179dd1eed3c8cc07a01a7a9052e785f1e38a854d77915998253c0a94d171277790a56217b21f8051c155f90ab2e102fe46bb0d4ce0e2a6825361b9c9a4234d14140509b063efb949c57860a7d12fb33316d13dc4574da949a5271074401a8372d7ef65d0873d12bb052595f100f6a2add029804623fc7d43101a9607d2447937d3bfe27b941d1db964d66b5e23914cc254ffd01400864776801ac3a2d3b46c9d13a9275e01d20c21543fe908a200a016450a338e7487e3f3a7073b9db312d6b3d532066612d6b6e1d5a81059120dd1a191a5d4c885b77f31e0e1cd8566573458c5257f5a041adeec478a458e6103b481b2e47b27e31e3f6c35f929cbc68b7b3d91a416b9d3be058ff12d1a6e74c09dd5f513ab16f5ec93fd93b66d22a201ec5f11dfeb7940920eeeb0029f26e076e3787311326f028961c980299983b3b3ee030742a5f2039b3a66d2ee3b357505e9e295e73d53909cedf2b406b6cd9162acf545e0240ec5c21e4c341cbe9da065dbb63507ac115571312c414dcbabe2b3339997129b2a82ee00cf6570e6e321c12a2cf7e0c45425bb056c5741ee1272fd47fd1357015a731a9f70a418242ba26bdef8a474e95ce6999134b69d5aaf4228b4f6e57b004325eb3f77f01171cd12da4ef9b127cda4b674afed46034f1675238725669cf2cb03f3e82b752f8a09a76f7d53d299c212603de730437edd36d758484952c38f13245215f6308e414c81df568ca2f3ce209560ec36642f4adc201ff81391a25c4de370c11c9649390a003e42502497f6ea57e079e635cffbeaa7db9542d586fde5a36bf156d27058b6c2f6959404f742f8501b9a6e52a40956e03c8c8762e5acfee3e114ea622542a8f0412abd0611c73a643c46c622be520af5d0039e51c45e2e3522b5199283fc21c09225c7f55d500fb3ab17cc7601475396468feb0616ddfb8564350954d41e1dd7158f75b0b45410c1c22ae2c5994aa7768a66b0b5b6fd5be787706f8255bd1f510f8699d61e732c610ab14086352c9d712cb739940e38bd93ff03ebf5365fe391edb629849246ec70929a52e54f41b6e0c91aff7408c856305fb1b3a48cf7eb042b490ff5c623edb3ee93088209eca4928b5e920631a9f2506908abb19b175a3401895970cf551e0761f73270b7e1c5b1a249da7738f81804e171e0a0ae507a70a9e83e1593865ca4af109820d48ef631cf0938370fa554410ad7cde1ff35b3127a7dc8c5aeb8be811c995832a1cf39b179008603ca05fd1284bc6c946e561e24798a30d2ef03e982dc2acf0791cfdc550abefd96d1725f26e707cce0c3b9c204c633b874b317cc209e1dcb27c11f36f647c995f76946903407252b038ed4c470096336f6219344d48a02df2584a251446c79ea90820fab335750c345f1e95ce749617db26671ea20b549f980c3649d010eeee5c3934cc316bdd32a9092c8e237a99d571523a2edd310712e16dd8500c59a1e9e561108c041cba86132be2b7072f22133362986b48453f10d33b0819a665a0df2443d0d0e03c904a2c530c36b006bc4bae4630b1df01c95f7639a279705fe7a3f913482cd710a43fc853872df82c0547632f71f59735849f7c0d487eaf73ed080053c2d8fc4ec1ae2028ceae4529a787d94a6cc1e8520dce901477a0242f1939f316df986b1c6f910416af50de592248a327d428fb075ef9080a4c27dc6d0bf5e50638412f3791cab8610ac6ed1e869b360622af687594ef0f56c2c34c732597283b3b5085698ccdf12011d79d14c6bc8628060d281ef5d33c218c8e8877ee0ec832596f7b43ab360e0f55ef2e07d0566f6608602b075b88330185e0d52ec174f35abef23628b3e68562c9d1447c78abd26df7a8775493d412618649b0519b983f4610c25c11997dd473eef8bc63368429539edb4a7ad3e4da2faede365ccab4ef2cd1b69e56b50982198905cf224ea2657b414e094ff77df34a93e0b47301b400191cc0d251f04c6619398b1f65a8f03b1f1777f256477a01110db44741897cac785f336447279b6308c0b2e774b89291628a3a39235f60721abd7a1f336340ef4feed4c94b41f39d1841bbb81b10ff153296baa03d4687a91a659e6d4e099abf35a22fc82c49167249cd14032ba2a4df2faefa5634d94e15129901256c162f0e7a4e484e42fe1756362700851f53c557616e60af18ec886c4e83e49d336299fd18bba2f95cd589153ace585e59db5fba3a0404ed4ba9b08370df8a6a780194f87d9b2b052c588a4670ead6ab5dfa119e4a44ee0e09dbcdac0e883130020b41cb1e71e3f32c2e385f77f07054234c2a806bb3490230b715b9462fc1c62bc9a68649eba0847e50973a57f460eb2e5f41f85bb182fa48c23dad4b7efa4053d306fb7531ccaf009287bb5b32ce9a0406e45d488cb39b4802c9d77072c93b2da388972de72d584a9320ea5dc060f8447694b43a7873884ac275b37125bf2d3f469b214dc009fd4c65e1127bd0025b73230f295dea6c5600a6ece5312b880718bbe03102d17efa2c8d7d6424b77fdf0e03a38a7d5c354b680d02c4758857c46e8fbe6d1489cd88098b8dee72ca373c2e5137963bcf9db16dcf32ba7214992f25b0d90b6f10e3c90fed21224c34784d3f54b7aa0029da3624e36f53363a31d60b60bab628e20ee14ad2508d3556217447b2d1490ef895f503f6ce4148e9b0325465261e363547c0134ea0d13c4e9a9013c215324c4998117229974b5ee62a2c7578331c0c9bab8854bdcc201d4b39e6368240e23de619541985a55d752c87c20c83d983454799381fb5eba92bbe66ad517048601eb79f04568221431c450ee37d944f584532dde1729e784729cb3b241f5fef93025bc3646f365071469352102071b7a66e26428c2fb59d5861862715546f93b44e4b481c51cc5d81737156887845935022c025b0037e6d2a3b0efe2643691fff53289f0d44152f77353d1ab36df5672767b9bb070996b69c6bfd891f31fbbac1380e014001488db240a2ef824c8ed14a75fa5002163073712701f6150553ac9c20ac5995498007e24a63df4e0725acbb7db11f36100f38f83b8025d85d24d0b1078285023d0b98494c4a96d601e7c6d9161d91584ab089040f47bd90665294ba25b8287c6d9d12966774b2e75216d1373041026d02d982e234ed58db548f372357f9dd316549ff5f5044553e584469b135d454d32f4b4a0f612ee631585bc066351b337f4eeb2c432a4d3e0625ed3a4c6f74be65236748df118a27985c70b51002255e7d60c458b514b07a0606033adf6a2a4d035174936b44c3147c29592ac00fbdcefe50ab40b26d6254c572d8e59f61c928195cbdac377a34c15f544cde2950e16b76283b2f6d4af0d52d12ef407614e74a4a00d54e184a14212c79c26bee224f25143684ba9e6513c37b5119b6357977f5d35ab814f5363edbd7548e47533514dec91ed3048423d5d5ba4084b26f1e6615b11b876f673c9241b8429072a51731305f43fda5fa541dff0e245c63392f06e88a6fc93b1b0f3faf6a4c9224501cbd3a715875eef137cfe9bb7691779a44f3f0de4f54a1745077da6f53fb29eb0d06ffc455a49f81363b9e554288bdce6b7d61376b21286e31521c004669774d522d90c90aa83b3379a5e5a478de4b7c214f4bdf03c32052314ae6525660aac81c3e60eb2be942251e3cdc3d572a3e0f140f3b1032a14ad129210341176f2dc735b756aa12b14160725411c332372eb83f0a2ca8327add3c15a1f3373cb2af1a0d3779be6c5494d0426539022a20876770abac4803913fff21d9c8dd40819904117a6d80149c39a2495e006810b8ef3f53054d102ba46de9464e4749064608e05316ffd450ccbb2c76b03ae6334dd230661bfddb76079a6e3e7a593c345dcca80b2f49e57500cb837cc988d5080a70fd5d2472c463db1259727424c77ef4611a185688aa7ec0a1c65769c33557f0c1ab422f64c41a24f2366784e1770733a4c155fc60d83cb059952182810e0ec73ded3f7fa39f7b5e8b380b5449c054c02d417aeea52a6ba58d170262981c47d31fec4355eb071acaefaa5b5be60045b3ff7e50ab2e1678ae198542d9dbbc0ccf25100890e5587a6c9b4f0abb423d389ed6c86d48b98c497ba0943e2678e96dbd5faa5a39877219cf2764019aa8de2e09e72a31aac7af4d3bc1ca5dc3137c6440ff3a16c1c8d724d8ecbf12e9504824558adb5cb8b5044c61ccd7755bdc233b48c8fd1829cdb74820f07c20070f8e233e51951f2493405394527762093e317ec54828448fe0200a41df3a02e1d4492fed3d174b6da51106726d622b2b9dd70cc873a43a46ee8a1ace777d08c18952723950683b66585117c5303e635083a05659358f03da8803534983fd399d5b3109c2916a78bf95f75fe741532cf507c43166d8bd270059ec6a8ffc9a4bdae976768c69081dfe6b52239c797e40e9488436ba2fd25ceeece00105d7bc44f5842a71785c856a7dde3b10f549a97d54ffb5037d7af1111036615099b0f7107226520728791d42ecd42366133e080a8a9eac4dcdf7394302139e30cdce5a6e9aa612551b5a5e557be7bc21bd0f7764cf52cc3f8e87151eba1d7366d23004076c797f01c5d365052fa48d62f1857d19eb7fd319b1541c0d66b1f656577c7d2b993101726765520786cc6640e103c076f45b4c6fe8a2f0195128d54fa09ca16243db910f4465e141bf12d31f4c4218096444f86a01db9c446dbd0431d3acc7578afbbf49b739a0168cbcc909b20b6c4ab436297e51f53933d1696161a403fd7ad47ac45b0ad5b111bdcb9d0dbd1f00701f37b23a78275d72d539450e232f045cfc16cf24d3c34c50ccc11925d3e09d72194c1647bff17f17d0f7292315787f78fa5d8d588fe47e1666556a7dfb6da4555f51da6febb8265ad2a9f255eed6667a016f6f446b4a5556d839ac17b985023f7cdd9625757369097ec1762dbacb372a0d6ebd0c1dedc06daa02b2024c35e10670b06d6c48ea540b0595cf005ab6193ea8d14a677accf01e0379c0274fa7e8226dae96400c52270fc282132912e2c11d10be8c47262554412589412dac8c4e582ca44d09d23c04518084d343454211764b94455c719e1617dce3431abe5ac500e653865b614aeb5ced32002357507d1735ec77032cede225ced58c2d681f261fc6469158a0f05d36f2e5781bf72c58267957d92f2209fc3e8b5c011eeee5683bdea28a158547a92e95d67f2216987e1bc0f2cc3c181b4b7810f79d2df229020de0153710c5e293790e5ad33072bc3909785c7746ec93b53d46f98a593583ba1a95e3ff6b690bba0f5572af7e64697926951f4b2429346c5d1803010f302b860e75630779bff5ed1d90ef5c3f89f3a765263717333724b51c22bc171de0be272b59a6ad5c17a6ae191c31952e2f1863579a011717f5ca536746e52913e14ecf106964bd5a06a0f63471ab60678d6b6e2361935a49fe7503185e3bf523b4ddb94b921aac43bed5b67cf42a284d35805d02463f642c49e384631296c30e68f90a589bb899548df43926d8bdf86f941bbe523f14395807bec146d824d61fe530ef0e227d7875ee35085236f68733b5791a00795b47186def89548f6e101bda08db5b8f20dc6d7948ee5048e72a73119f7046b4618d7e8ee76627d023bc0d7610df7151c048385cd5f638b4849a18a819fc2b5191e434acb13a7a3656a03fecb7363971cdd547fc213375b33bb71a0298f41fa70cca2dd38a8a6d84124d768e67794b3c8e210ddb6e6f3a8ef35e566273e122601c534dfbb99c0aa5e4873dc719da047155a6437b2f59228c2f1b7d2cabe61f3c932064a777a62d62ff555fbdff1c258b968b35e34f7c36acb05c4f8efa4c0fbf36977a40bd1846bb5a981ca42d586fb4e6363dce3b401bceea87635cea5f03ed563539afa9cd08a181d0739c5bbd3f31a2da0828c98b57ceda4f671e188c01862bb06ddd8fb41b5add2d563cfdf6510c0c2c0275e1cd255a6a9641daaace5981ddf1105f234f0cefd1d974371a5f6c58ec500404bc231dfbe9ce1b58cecd3cf16dca765e276204baf4e47b5944226d45982e588ad36666e0bff32c01774b1f86a24c34484c45110a3b1b42848bf959450c255234ed6f5d80789e569c41f807f1749a57250d1a7b5e1e11351131b748890cfb25ddec5a074794c45dc96dd744f0857b00f651db472582f04236490a059174e53651fce3183d7e8579302d4f089537e846c79a3a5f999f7e7b30a793177245e96f8327e678544c72114c840a302053167a34a4405a55b0061addf9a65cb92d886a4fabd0326af52d5713104d42b1375c6a89cb7b312d508d58709ff91d74049c5b8749542693f1f02e9a9840551f13994a07764f6e7ca1920ef028281ad54e8b30317d242e7d2e84419a5be04766a4c279f9f1504c73fb3926f69398032101e04ce1730515a2d0c04e9ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e89953842813c530b345fa66c045d456549722e408cc26d14b847c973eefa1e6af3427704e3b270528497fa78e4c3c07ad9691010afd48365ffb65515654a4f056427c226d728d614b761b961175ec664166f076e2af405605f0da77a696ed6442f0356178eef5164ab2bda433f9fa7046cb15459fbc949159c7c7f36e0fb7f3003392c0a5f686d3b8b14e85368cfd3746bd7ee03cea6e13fc38a834c2950d86712f242778a01b42bc95039047f6949593d8f7d30d5474d1b874db8369b5f927244b14048324dee6a34486472507b4b5c19983942b3f7ac093c71306f5e7d401cff47b45be69898520f94166f9480b84bd66f744d61e9fb1f2815a8162aa807764475386d909c05457996a20cb17fcc03866909483009462c4f9f7d189eb3b6309ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e0f5abb2a43ce33322e248b084044337e1ee0f32289731730c7545945b3afb219a72d242f0e05104e7c06641a2261456af7a4a700d026c710ee538f26c8e070245b60656b88a0e15c8578b03e2ba3241ad64ae1621681c8392b90a93f82060228bda9762f6239e73b99576d402487695009391526fcf6306723854325f5a5e448ffc5f5520e4ca9426305c03c216f8a4383302212d854b0233518d25cee4aa45824480951e3651022063db557a0efc547ecdd94462da2284eef30c91af454341ff779ad590d6f737a32cc4563fc31df724099555ea6dea1306da7907d890f7617008de57637aec45c8206ff06265529246d27de749792e01b93d4a55bbb945c061c855474ca8b772617629902cc881b0071c85d4d6d658d16ece5554c9a772126d2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c3eb69070823d942399bcca0d2bf260447363440123975c5129287237c90eeb74d40dc02278938633cf22fa76018e22776f91c75591b4577625f3e7247af8ff793aafb27a5c60af4e1872be1bc2c6d242a3ee2c276c71c23aeacde055f194011e0bb9464f47394f427e629e09b1785c70070f8372b4999d62cb39b5142dba83114ee4c5447596387e20510c2fc5c6606a91c94352d35c6135eb9eb02ffdfc894cafdf88170a6eed2657a48554d1b7642b2f172736921fa75dd60c1c626243de25cad9e02da7e8ad34ef628f7edfa9271a08fab442284cd9630ce4ac2289c92553f4844a495e3e5c05e29be75640b5273f61a1214c32153a6a452c4f036413ac301c855474ca8b772617629902cc881b0071c85d4d6d658d16ece5554c9a772126d2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c5c7274414501ec03dbab605c0e643e25b51b4330914a9f3d60df4c4ebb669f00ae713211b9623177ebb4c447dc53047b464c8f6cf60f140bc65b3a60e4e96170aaf90a77eade8d665fc12717b390d84c4731ff221516c960a304075a6155dc1fda67ef241ff9227a69365c6ad0f78e1fb4ecce34c2454f79d638b12623f2757dc2387c3c30f66e7dc76c74098840776c3416ea13cffa4c6f5620a85423902732ebe94710ebe876352c1dc25490150358eecfb35a5a70b65255cbbe2f543d3c4230d9560eb3ee726f49e191475868166c57ef582588ab9a17f57e092b46591c62b25ad21bb2fb645c5d19737165232549a25d7a3230820e0520f60620f16cb21a64edfb303ddb4e10ce95a50f3df46d5b6e815f6e3958a20710e8f45e59ef9627af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e600de71f9b12025d343a5d6e9552476f15dfb4584773bf17dd9a045790c7d54fbc730a1a743e382c7ef6a80cff8ab5319c24096088b8d01b8451566e653fa9672559b013f1a15275e4da3b012480712cf26da508cd7d98757d312a4f920f6b1082ecc954c0cb231c99c57d42ffbb235cdd65ff6b420aa5633480ef6f56095a6fdaea535858db4c03e69acb636439be0cddcbaa1f9235203f0f2404029818596f7ab60b61c1ebdc5ced8c2c3f7347194f2cb8887450c0006e49d4ce56f215217712b5f2284123dd03229c865bbc71e053108412537129ba071982b4338ff0504d008de57637aec45c8206ff06265529246d27de749792e01b93d4a55bbb945c061c855474ca8b772617629902cc881b0071c85d4d6d658d16ece5554c9a772126d2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c81fb8a7aa14f39544c6d3b7db0ccf56b7fa45e798ef82607d6dfaf5e1d2f511fc0036f7390e9d83cf2382f21b96dc2581080a12bc5b1d7709ef74c2c092f5502e451183c6d71c1373fdbcb68cdca926eea8d4076878d3605dbd54b300e3c9e0aff21f93c2a2dee2c0a984919fa9b6749626d0a50727dbf59a88db70306de5940c2f47a5d18b2117bb1e27f48631c7c5e8359c36ed309b50eab1ed402775a496ebc810855d8606c4a8144af0e1a19a837ca8e1a69a139113f39a3c1140245b52c85410e7b3ffcdd08efa36109eed8286434b6b64a14339f5e2b5647186cfe462198e04005b39aa501ad70553103c5630cd744c44faebcc36ea690a444a84ea8367ccbb84957bd2331d8a7d8202c8fc35cb591cc76c97b232660a6b0637e3a5f72af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e61908d4c565613183a3fc61684ae363946023f0e05647e408f2fec26fa590e4613b8e33c0aaabf5eafe5c72f3195466f1df6a172faa3b24093f3aa1b98ef687eededd322cee1563779b9777c95ed8f37bc566141204e13544958cd5aa22e7b2cb5902d1c94f1b63bebbece7b9c488f054e3dcd4601172774700f2913d72c616cc3699c1c6ed68063e82f645eeccc91787121aa7d6e2ec772d8520258d1161b73549e5d7a41cc9c6f8754224407309a66aa0f0d7000c6f4713a40c365f43e1866c9de77463e2f02653e44e205b78dcf3a125bea0400b54b4cd753e947e0d13550312dd908d6048a600eef4272852e1551bcdf166315e6a16da7d1492113e7ce72f2e4355a92e6727c239b5c7eebb98c590db79b03407f5b208e662f4a6eabfd108caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c5c3ebe4bcde04709ed19a85e127fdc15cc6a6d253e6598370ad5fa0db6ba847133b9bd7e8212c142e5d97871aa7c3612bb547a429300936f63dd930f7d7b565d621270162f0aa15b1e8ea810e835bf453dd9461bb14f4942ebe75941ca2c137a5b9c0b509414893666131c469370782dbf59852c89173534e73b27515b67380003392c0a5f686d3b8b14e85368cfd3746bd7ee03cea6e13fc38a834c2950d86712f242778a01b42bc95039047f6949593d8f7d30d5474d1b874db8369b5f927244b14048324dee6a34486472507b4b5c19983942b3f7ac093c71306f5e7d401cff47b45be69898520f94166f9480b84bd66f744d61e9fb1f2815a8162aa807764475386d909c05457996a20cb17fcc03866909483009462c4f9f7d189eb3b6309ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e77816307673327601b4ed67b5035867c963c196b1c10db759bc5a74153795e1bafe22b2e6e7bbf2c3f6c0979ee6c4e06c408e67451e8ad2b9802fc02a60df51974f51c2a2665be4bfa06dd6d2cab7348a4f7c167256da46da5d27b2f1abd100b30bda3413015333fd2be8b78300f5d42d8266a53caa2383b37564b6fb5e36f47a20b6f7e0464e51b829fbd5123852128e04aa9288090f8770606384540938f52a2e77d75bf7efc3f431e2f1715b2bd27fa6f501027c6fb106329007e496b5f5330d9560eb3ee726f49e191475868166c57ef582588ab9a17f57e092b46591c62b25ad21bb2fb645c5d19737165232549a25d7a3230820e0520f60620f16cb21a64edfb303ddb4e10ce95a50f3df46d5b6e815f6e3958a20710e8f45e59ef9627af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e18594f20a550d675ba41c02a13f05c5329a9d46167772d558966632400d7342cc16aa721867f4027b005d7242738d6681d829b566a1ef30733635839484c293ea4879c2b639ee36338105d256be37773b3b97466f6e10e63bc8da33f7138706b9578000869bfb80bd88e355640797001b1f9176dd9d03056c8f61a494b6e1d2691739d2bcd9f620d4bebdc11e4673766ffe52e0da49f9a5c064b32652e84db3daf56484dd574f65fced53b06b4f7584d56d6294d2cae8e59bc3aef17d1c6a3385cc94e7045c82d73d93e6370e11b615733e56d30f373ff69271b606d0eec170afde63b1addbf657d94c733140fa88467d8707c635ff4b843dcb07c78d0907e55c2696775558bf52da8d505359d06ac2a0381b558a5f47f18103fcc2a17a5fb3e8caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236cf494de03b9f53201714a950145613709710a444df897c369c7d6374daec2d97e7db1e80c9227c430faae80261d6b891899aa40045580991967574847bd4612505d44b5244b18e02ee0db6f663fb92163f3908a67a74ca9339c99293b9f499717e401285f7b45aa762a38422ea1729842736e2844c670e431ceef545d7c4d9269e267fd03ec53af382768fb4f3d438d253ffdaa6714909b6d62cedb0155306d6262db915da5525e1206ed492727f0f85bb1cc121f39449e33b88ba344309d4c6c6073df15e6b9ae4b7a8acb15d0cd6d14f09a4e2881fba224d9781a3b1933f839164a461ce5c08b67eb111e4521e41f40e384df6021075c33f3e7e7402ecc3100c2696775558bf52da8d505359d06ac2a0381b558a5f47f18103fcc2a17a5fb3e8caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c36e61c3f3d2bb1128eaa8649b9f4c168383a6313c4178144c0ffdb4d07547a65dd530d4ebf039a10835c1c0ad8b80e536fb857068333920fb1a54731df51ab407a26713d266011644f24c72fd362d55d274787066347361d1896470c4d48e01d7a80b426799ae64a0675f23981982673702afa0eb2eba341b78fd96985931c1ffb84961cfb5dc379b14820107d78882632a7803c4a1b511d34d51864fcfc8c5a4a79d60843296071ca8bf814064d4c372d3c2f2023ee7d691a728f0c1d07f06e5cc94e7045c82d73d93e6370e11b615733e56d30f373ff69271b606d0eec170afde63b1addbf657d94c733140fa88467d8707c635ff4b843dcb07c78d0907e55c2696775558bf52da8d505359d06ac2a0381b558a5f47f18103fcc2a17a5fb3e8caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236cf30d9e270c08ed4b03bbc11e876bc2744bedd348c3876e5838561b781a24eb35bbad5543ebb7ae40060d1d748842f740b9554d085538574e5c206450ae98330a2fca1e63e6c3af756fe23d63f84a4c2b493ecc1f515d82589519bb07f5b1f407dea4790de61456418da1010177cdec587025dd5e7fb67454b9a81820959015494904571481140b4ed258f8594cf6f06c8fddb15a8b05e525a21ddd606beae96790a9204d01218b09ff4f435fb7c4896a63e3f7117c14035b875bb0092055be255f53a73145ea0a7cb38c2b3dc5408d4693f8813fea532d016950697a0bd1855060b681536892df4a2a180233a668c966ad0c760bd75fdb59238bd57ce57f836cbcd596299e17bb3d84ff8b6a83ef2e61943e46793b89d52c4ea4b10f3ec5576fd2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c812a3e53b9dae769de394f35da7b041580fcfd0c54a892707533101a9bff704084126d329ed6084523b1791d52eec47767623f1cebcf586410672a6320636d714726a66125625e0b622de871d301497b22f69c0577211d2c35582a6c626b2f5bb3fc2c47c4aa8d2cb3a20f38b2fa737d72dea843e7358200135d647767ca4914565a892b33f13b3d2edf0c25bdc5b67c54024609e41cf20c1fbfee7c8ca3873931ec1d53d6d0904eb5e2480ea92b67310dd9ea756d8440745b662a78340d496fcc03b549ab2cca4d7fa4431c5fe0cd7afb8f9e6013847d636317793a9a68b532448b0225331f02473867e13b1bb4f74cbb041379f73eec305c71b9699485550d7ccbb84957bd2331d8a7d8202c8fc35cb591cc76c97b232660a6b0637e3a5f72af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5edba2793d835e404b2f64db2b9167e011d9f2f077bb7ec015f3ed210a353e382a2c9e170378a3323780f84a6af531f843d313f21171c6ad5916003a06eedcd26b7da4626dda7fed3d9f31b276780175280ea7e34b09d7084dfc14005e57864e762f9c92347403e338ad0ffe580a9f7350255f4350e6addc436ee4163af755225fd91286362d9aa3543251427e0cf91824bf0b967259ee1b4875076577362a1f2b22941b15f53edc112342173fd9fbe32a064b6b7e27508460e450633eae8f196f38269f4896df383ae3ae5d59f79a85212f46927807735232d3233c34412b7e5cff47b45be69898520f94166f9480b84bd66f744d61e9fb1f2815a8162aa807764475386d909c05457996a20cb17fcc03866909483009462c4f9f7d189eb3b6309ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e4b9ddc6da0a95b45758796384efe4923cf26f26a4e8be815bb039a18077f41595f7f9f06f38dd850482fab246a087c22b59a88767d6955424bc48e7a4a00dd667244df3a60d03625fc3d7308a759cf42283b941c2413c86feecbc75572ddce6c5e39544946768879b31c3f1c11742d03a418ab4737eea559ab1a0a114dfb3f75900eae72af7282445b6a9e59c7fe712688c39707603f0f6eb3118b7ef531190faf56484dd574f65fced53b06b4f7584d56d6294d2cae8e59bc3aef17d1c6a3385cc94e7045c82d73d93e6370e11b615733e56d30f373ff69271b606d0eec170afde63b1addbf657d94c733140fa88467d8707c635ff4b843dcb07c78d0907e55c2696775558bf52da8d505359d06ac2a0381b558a5f47f18103fcc2a17a5fb3e8caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236cecd2081cf0ab664be4a82e15b10eb0078a281d015d93b156633e6847d430e6630c2e591a4460d8503b61c437d593ec4320c1ed74bb46fb094dc71e3a0079581aaf843967b1557663094a466627b7f1574dff1875e13e4003210fc11bd962df66f6e7327681fe6b26060981672fcb300298dfc775473a1d733aadfe69aa430850d7ac38697e33316733d285212a60707129b91874245f1f08a80293440f448a2cf120ea1e421300559138406ed203d01b2634650d4b85604078140722de8c48692eba4e1cff06aa09ead0385b32ef4d25b5b0b86d587f2a7ce91fb623a182af4f48308c595dc3c53fd89ced72b8672962a4ce204b7d89c77c48d2cf23856c773af2e4355a92e6727c239b5c7eebb98c590db79b03407f5b208e662f4a6eabfd108caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236ce8c90c0575ff110925b077140ee2154b4db0c7461dfcf23068457f18a344ec523f129a2e72b2410414b59b6ea1f1154a5ad93e208639451fceefc170b05480702c4518105340725bc305dc1c62e9bf088a9d770295082d6770e759759d4e1001eabf357c1cbf722a378a8f638440fb7ab4558c708af16b4fefbdd101a3133e295fc7ba1882b5c2419b6a0211d9905d138749aa667bc64338cd9c763e78e8ac6142e08f039a35a365b565a275107be221ea977844f2bd500d805f701abe3a13154f384121ff92cd665a086337ca27072f56dca356578bf6759f841d102706295002588c049591dc042a59c9000385a92ac255dd225916004816e5ac275d4cd1094475386d909c05457996a20cb17fcc03866909483009462c4f9f7d189eb3b6309ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e59c4d35a54dcdd19edc791785187e13f17cdc54458a492007672e2686433bb2e2cc9ce32b1815d2b7902607d7fec9a014b4a89285b44fa70150cfc6511f33e5e22ea8e15a74d4530de76202b0aa36d16bef4d50c33fcf81971abd609a9c3c17a090f480499bc7f2ca1d2cc514654e37b6c211f39fd24e464257a7f0bd34e755a2a590635aa2c1e5a2a870e2751c6f0130524af6abfc4650ad72dd1076cd12371789bb52663b2144c322db2786e568b338a2ce268e7cfea538e43711f9711742d78316f3ac6552472feee7879f4ed2c1e1c0d890db38ad71a4eff8e059fd25a07fde63b1addbf657d94c733140fa88467d8707c635ff4b843dcb07c78d0907e55c2696775558bf52da8d505359d06ac2a0381b558a5f47f18103fcc2a17a5fb3e8caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236cf189dc5e74333d56c7aeef630ddeef07d88701498a0da60aa275193dac337a672c9e170378a3323780f84a6af531f843d313f21171c6ad5916003a06eedcd26b7da4626dda7fed3d9f31b276780175280ea7e34b09d7084dfc14005e57864e762f9c92347403e338ad0ffe580a9f7350255f4350e6addc436ee4163af755225fd91286362d9aa3543251427e0cf91824bf0b967259ee1b4875076577362a1f2b22941b15f53edc112342173fd9fbe32a064b6b7e27508460e450633eae8f196f38269f4896df383ae3ae5d59f79a85212f46927807735232d3233c34412b7e5cff47b45be69898520f94166f9480b84bd66f744d61e9fb1f2815a8162aa807764475386d909c05457996a20cb17fcc03866909483009462c4f9f7d189eb3b6309ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e9430872ded8d541827d0010eac6d8548f701445fdc027a51ea7d9c05345214363e060a2dd82e1214fe64eb1914db2e309dfb655e26bae46a732f5651bdbde06f64b47b4554563710ec15f12fe8c9b16add4dcf439f8bab50d6927678ac42c15c9b5afc0530dff7140229b74bb96be75f253ad06b1cf57a64182bf7599d9e617ece40bf7dfa431c23d74e4f69fd32fd6701fa553b2d870e6dd048b37c2da0000792a20c2fa22fcf5eb5ae6e055c504e0ae39afe0b88cf5304a77e6f5b21f39c08dc7c4865bb45dd41ec8cf2622bc0b661217a3e6d15ca3b55769ea602a855703d1a179316b17b27649c366e28729f500d1778a06c77aa243e4c01ab71bb81803bbcd596299e17bb3d84ff8b6a83ef2e61943e46793b89d52c4ea4b10f3ec5576fd2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236ca701f169a5f882250ba6970027022c007b1a0464e22824584efc130c49f7511015870b4ab145cd290af8a253681fab13c7ffdc51334ccb6e9282d55cf0e97764307c6e205078cf771d208f2f222ab819fe399803a711564c8b54bd05ef654457adc15e2ab45e923756360a559b4cd75ec5c04658eef4d577adce2e080b5f615df0e96c1369fc6103ba75213bf40ed10c254bc43aa4d9b43f453d1b7460b30e3266736c1426a1847d72f78528d604e21d520d88701cf1d73f8560f9652b2683292fe9b57c3a878b0791ad6605f43a0a70e861b967c89a6f27a62b8821d3029b6460b681536892df4a2a180233a668c966ad0c760bd75fdb59238bd57ce57f836cbcd596299e17bb3d84ff8b6a83ef2e61943e46793b89d52c4ea4b10f3ec5576fd2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c5ed9e904a2f22c4eca8b9a6fae2c606149162a308223b23db115001cdacca437dff30d79671ae6064aac4518a66456514fa20d2099ce0834842242611974c50a5c81d31d0a70615ee861b25c8182a81b9a17db6d436863654e4d842b045af03cdd34d04adb628505569a1d237497ff5b8d255d46b482276248e53308c419dc7a2137cd3ce904bd4c28972d48f0850e19904f9309ddf16d0c355cfd7d03003e465a6a630a6044bf130283fb610dd56d53790bd64d361dbf7a8ef5ba5bfb1b4e60d0636978364a00671e48f04a9bdd4a27bd4a606ce09b29280914603524a3e758f4844a495e3e5c05e29be75640b5273f61a1214c32153a6a452c4f036413ac301c855474ca8b772617629902cc881b0071c85d4d6d658d16ece5554c9a772126d2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c5e53dd1b6dcf6256ed11ce1bdfd1dc35f4ea4e41e8195c51f7e5663d91e6f92fc1f43027255d9d54472e2b47da52aa3ff78256548ea96e39247b0c27c309dc3ad67d6068668d36646ad8cc32eea9364763207d158b3ae9668723b95c0c109b18adc15e2ab45e923756360a559b4cd75ec5c04658eef4d577adce2e080b5f615df0e96c1369fc6103ba75213bf40ed10c254bc43aa4d9b43f453d1b7460b30e3266736c1426a1847d72f78528d604e21d520d88701cf1d73f8560f9652b2683292fe9b57c3a878b0791ad6605f43a0a70e861b967c89a6f27a62b8821d3029b6460b681536892df4a2a180233a668c966ad0c760bd75fdb59238bd57ce57f836cbcd596299e17bb3d84ff8b6a83ef2e61943e46793b89d52c4ea4b10f3ec5576fd2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c2417731389b9311c620a9c3a4790f636fa4a581254ba5f0683f7d022291c920918c4c50370712e57f33d594cb370e23e1679317684fb1b5e3deef70bcbd48778d3296d3133e38639f93396791ab5ff14feaa4f5897fba2674a2a9b2e957c381e44854f22edfcc63210a73e6066f35706feeb5c7bbc4a8479b1c7d95308d4f53999a1382835cda75f3a9b460140d5900a553b7969ab03e42ca50d1118cb17ce16549e5d7a41cc9c6f8754224407309a66aa0f0d7000c6f4713a40c365f43e1866c9de77463e2f02653e44e205b78dcf3a125bea0400b54b4cd753e947e0d13550312dd908d6048a600eef4272852e1551bcdf166315e6a16da7d1492113e7ce72f2e4355a92e6727c239b5c7eebb98c590db79b03407f5b208e662f4a6eabfd108caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c848de055ffdf4c790dea8001ce7ef779eed51f7adf1baf7a6a234e52cdbf581447db0f13a15f324d4c9b765960b78973df7b812ce203ef77ad86731879acd93ff40a474d84c812431d63371bc8fcd2726369833f00238879353cae7c88075a42cb0d36107857fd18c3034e198e44ca5130699c3378c6fb210513d76fae287e712843e42caecc1e2ea5af65310f94dc2ee3def22de382347b8cef4f63a49b754a5a6a630a6044bf130283fb610dd56d53790bd64d361dbf7a8ef5ba5bfb1b4e60d0636978364a00671e48f04a9bdd4a27bd4a606ce09b29280914603524a3e758f4844a495e3e5c05e29be75640b5273f61a1214c32153a6a452c4f036413ac301c855474ca8b772617629902cc881b0071c85d4d6d658d16ece5554c9a772126d2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c358db063765d34444d70036562f4ac582f3ad24016ed5823f1a4160f9c3cbf16b804dd52d00436730b8b466d970f4b50a4309d55c178c863eabb6a57c99f350896289373dd380a0b82c375444a08d3414248a468d56c760999ba8f72f704ea16b11253117e9af50c3cc97720366b187419003b39ff27eb633f7da1445bb92d659ed40d2df793510683912b27e941f34ef4bec624d7ad3a2bdd4c2a3eb06cc264e3efac14eb311520cd85102b9933f5435939927be3d20c58731b082d7ddad319cc03b549ab2cca4d7fa4431c5fe0cd7afb8f9e6013847d636317793a9a68b532448b0225331f02473867e13b1bb4f74cbb041379f73eec305c71b9699485550d7ccbb84957bd2331d8a7d8202c8fc35cb591cc76c97b232660a6b0637e3a5f72af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e35273372485ef8743a5141791fb3066426a4ab43e8285a2730e9b039c463c84409a2e854712c78033e7c415acd091b6455b5363192361d5a3e17bb04ca5b9822f3bbd9304eed08002800dc5a7a023b64d3559f183927176f3063750a22367b7ee14f5777d17297117066f744d44a1c653f882675f9dc9b06cf1f3c627e188508415c25218ba93667a8c2442e3ea5f6648654bf06ebc96936b47f200dcb751f3300ce481b46531b28e6185600f7806d65b79b391e9ef0ba752e198d59a3e3ba4c0bb7a8279b9d6d078220214157872b28213fbd638569f7411a50f737f51c693631120a3a16799d04f372556284b76311fb76d22707fa790d7b16e744c9a29f5764edfb303ddb4e10ce95a50f3df46d5b6e815f6e3958a20710e8f45e59ef9627af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5ebf89ef4f3e5df62bf7c7510cb8227d3c5e907143c4f21e1bf91d8f5c3a2a4f3526b7932b2dc0885648c8850215c955519ee7411d5a686d68ccf7b309dd82047117a0e36f57365651b500972d833b9b0d9279631bdfbf300d23861115f25f5a4ba2e2c879bd1e064ffa4dcf6750acc8387674137036fc3d5d9389573b636deb7c47de8001ead271782ca73200b6b80e6e6979c32d69020c3b2ead3a4a2ae7a2011338b753611cc14b72bdb928499efb15915dfd62d3c6ee51f041d0280b6e4767122d04538770fe2e6381da28793826456df4a649124dc848f5cf7e6df74a547848308c595dc3c53fd89ced72b8672962a4ce204b7d89c77c48d2cf23856c773af2e4355a92e6727c239b5c7eebb98c590db79b03407f5b208e662f4a6eabfd108caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c9047a67c86fa89086efa777effe446589e169463e464844df00975084f9a3050b9b2f00c7a89ea61a1394b4700c5b13f72dd0d2750bb0f048d922a5c0b9dec38d16123670de7f121c830fa5e5e386a1d77663809c11e2a4ccfc737422775510a8ec3df0afeb19d532d3a8f264baba738e74c444248bba53b89f1a95876c91d2d96a44e40f926af1b8f11425ccd90844abc0f1e0051bcd6558f1abc4b6e024d16cbb8fc2da7849478a42f516e06794b2ddee61d425364fb024fd6b67802a3823ff8c79b2b576f956d9b7cd674cec2e71dbb572332dad26d3a204e39257dcce747164a461ce5c08b67eb111e4521e41f40e384df6021075c33f3e7e7402ecc3100c2696775558bf52da8d505359d06ac2a0381b558a5f47f18103fcc2a17a5fb3e8caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c507ae969201909183510b9104cd91b7d4844c4794d350547953fd563e3d72a62fedb4c55f8edc4563f1ccd21c0fa325b7a1d1b41f9fb7a47e744366a8fc6bb03678c026f8b8d152b7a9501393b5fbe2d35e1a735059dd2524eee8b47bc4b76067f6d85365e3e225c40538c53eb547b524925854d16b06f615978d059acf9d7333ae4574854134741c597f4474ddd63057372962d7103f0070e21027e6fe7b4332aa4de2941f6fc56caf10f3e50ecd045cf37d2296305f24a67969c305b21c57285410e7b3ffcdd08efa36109eed8286434b6b64a14339f5e2b5647186cfe462198e04005b39aa501ad70553103c5630cd744c44faebcc36ea690a444a84ea8367ccbb84957bd2331d8a7d8202c8fc35cb591cc76c97b232660a6b0637e3a5f72af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5ea4b92f10af7669449fd6bb76e043cc16bbbd37498969072230044d19961f6268b764df58b44d4a2162811d3ea985ab5da1450422201c3350c4803d44f302854ac18e4419b6788b09f805157b1a1c882902edac60fd9ab342a3415c569d4e825b2913b63dea05c01975e193260787062cc505bd62981faf68891cfe3f4723584cd715b81ce6c6c3785cc0f2314dc8932da2c83a4d5db5b9380efe5e697f9654483b9cd60eeef26732c43e175773e9b651abc2c564c35cc40547c9413c395b417e4f384121ff92cd665a086337ca27072f56dca356578bf6759f841d102706295002588c049591dc042a59c9000385a92ac255dd225916004816e5ac275d4cd1094475386d909c05457996a20cb17fcc03866909483009462c4f9f7d189eb3b6309ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e509b5d5c20b6c4411776747a5519244433ebb704c518115f0984ea2fc7e88115ed3fb15c8529bd448bd5fc480f19942360f46859382bf06eda32ab20c27a9e6e4c0fcb40f8f4b86244ed2301c3f1326f18cc3951ba9f673b9d32eb7272a1df5d4c64d77d499eba02f612212bb97315134713342750587539ab24390adeb8123da30cc8490240a5137a41fd3beb7c8c3eb03a4b51a5b72b1360a72c4a0fd3c41806c5087ee00b0d39a46a5f0c53276b7ef0e2f263d7354156b88e7e6d7f05e9516afac96ff5b6bd5edee5fa1930f7c160e1b6c82d8ca81d10b9d6ed4d1501ad4a8691a218346ae6184ec88204c5468f7df4a2fd603be69b1cab49f66ea76443299a5be04766a4c279f9f1504c73fb3926f69398032101e04ce1730515a2d0c04e9ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e258dfc245a0be72ac244c23a056adc4a2b52826a6505132d63d3be781bed14019831de4264a2857cfcd0d325fd9457071033f30a4458e33fdd3e8705864b47186f60796da917023aa7147d33a07e070e0af1c55132cda37b9795781bab642c543f545d1975bc327c449ecb7d1943851f5dd7c375a0c7e16e623ac817e2b6f826e416352ddc4d2436e917475796379f43a6b8792b9e2c9408ebee9f149a60c3613950ab19e3c8276dca0b4175d11c650394a22a735989631f559b7f627ac5e171f8c79b2b576f956d9b7cd674cec2e71dbb572332dad26d3a204e39257dcce747164a461ce5c08b67eb111e4521e41f40e384df6021075c33f3e7e7402ecc3100c2696775558bf52da8d505359d06ac2a0381b558a5f47f18103fcc2a17a5fb3e8caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236ce2ab72316607f64c6f3c0000af27e15975496041453686010cf1b11e540ef7018125b23a9caed030ab4a5109ddb32a1e4e7c0d392f162d07a8b266596e2b462f8225e16cfcb2c96fac9b483e683751262917a90c138af3718acd3b499b97032f3a535c4463235056b1f08e6306d5410286627772d12c2c791f11f661f16d9868188a234c1eae5e380872491ecc80cd322a7f7654e6ea573c19035276a04eee7ee39cd65d8eb0d440b70373127ff59f39ba028e41c8da4405e499386eeaccfb71c9de77463e2f02653e44e205b78dcf3a125bea0400b54b4cd753e947e0d13550312dd908d6048a600eef4272852e1551bcdf166315e6a16da7d1492113e7ce72f2e4355a92e6727c239b5c7eebb98c590db79b03407f5b208e662f4a6eabfd108caffa63d5d6f20f2cbfc072a7ddcf6a450e01357dc6e541b3d59979a6ddb336f90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236ceb09414870d2e0669016ef3400faf17d59eeb251de5d2c3686d64c31865c6b580ace762ee2f8be2fa4c27e10ab578e1d7dba6f2435747979f0a01061e4c0af426a2f3c6c28ff4b675082f939fd3a491fe041092a56147b6654ed634faad6455d57413b6af88fca3b779f30550f2d26158d31b9599031e63ee742cc73673f8617b997630892f71c7aae07371da445f248555b56635eb7e04b80d43855e532864f6189114295f3845a0a650037688d58287748211136c29e6f8c4fe0350d5741485f53a73145ea0a7cb38c2b3dc5408d4693f8813fea532d016950697a0bd1855060b681536892df4a2a180233a668c966ad0c760bd75fdb59238bd57ce57f836cbcd596299e17bb3d84ff8b6a83ef2e61943e46793b89d52c4ea4b10f3ec5576fd2d0a9522709b2149cbaba4f3932ad4eb641b608aebc1e5d93aa5e4af049c76bf90e633824d7711fa098160828fc1452e8a7c80d3da0d66a8606893639ee236c5b597c39b73e6a06950be72ef87f24579002be5e7d930a112a6cc41f42443b279570af6e827bd40acef93c3c86fed779b85c1e2b5930d11144c4ee3a43756738f26e2403207634547229c570efaf905cc26e016bc07e693e11f7b61a8ab9d84d39a21e03ddad573589beb414ea3ecc243e49671875bd961994f01c5273c2d02c27ecf43b5b72d019666caa6420cbf15262b23c36c0cdc41852117145b9e92102ebe94710ebe876352c1dc25490150358eecfb35a5a70b65255cbbe2f543d3c4230d9560eb3ee726f49e191475868166c57ef582588ab9a17f57e092b46591c62b25ad21bb2fb645c5d19737165232549a25d7a3230820e0520f60620f16cb21a64edfb303ddb4e10ce95a50f3df46d5b6e815f6e3958a20710e8f45e59ef9627af79b616aaa9991e9d2c3f7285b65524c9b5b737403c170e39dda655d61c20402122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5e16b3b940014120007ffcf9091fc7ab61870e613ccb74954920998b2676e591183bf0fd260fd1273dd4c863373263c443f6408e1ec02de44097b64362930b0c452d6bb352cad13053f6a1fb5e6e351a5894fba0585c78e84e1749f00e4543bf5fbf312418b204d5261f9cf314f26b081a66b0247e9bb1f517fbc7c848440eec7107d2c2018f60915bc23bf256201664040080d342661850128f4b991c8abf5749ba6fdc3b06a03e3cbf6f7e259e35db290b74bc7ceb7bcb31e807974dee03f174b9318127f26f9a015b9f2f22b3e54724e61bf574e207f304577e3271dba87c3202588c049591dc042a59c9000385a92ac255dd225916004816e5ac275d4cd1094475386d909c05457996a20cb17fcc03866909483009462c4f9f7d189eb3b6309ddf36721a66cc09413d481ef3e6b555440046759e17fe780136534d1373747a2122420596ef126bd0b5bc43814eba021251ee213e96c5476332cc1b127ecf5eda8c485a7186d3385909602d3f54185c5856076cd0d9962b02f2866755bc021850cf1155c0073944b592cc46a6520479e924d43464dff62f3875c71d74cd234d7498c73d7fcb9d17158e245a629b7c73c091e87cdd2c9e3ed90b6e551b05e93d14a7d261da014a516481043e95c65a26fbbddb543ed8d62ab795273acaaadc3b1024d83d89dc7668f936aa6ab65b18562b93715e89c5746093923317223cbf5a20170b0d65015b5b0f06297b8b3b20092fb1994eb95dcc7ea3adc5312673c415db38ea6c060fcd13a706f609d43b8848a6d9b044d0e51c50cf6b4d7a0912a6013df4f60911e0ce7452242e52b12b5953eef58867ebfde017f94b21255580744033d541096aa6f5458dc7da2ef8bb270fb7ff33035799e817e5ed4500a4b7812dcfef1d0160b8fd3d2b28db4b6856e214e1a3f34b524cfa3532250d1bf7cb1c7c472f8a33ac4ce212937c6521d5ec484c548dba1b24ec385864f8307afc495a38a8116e05a3cd0822033dec61ac34af5782ff9034a0d4b455431c113121a03679ff75381d68141b5d195d493d7c609e6c94a9b5652b3661686506b429cfeccf20932593029aa2756691b29816c1bae32848479a0f71e93c34d0d8ee2304666a276c7eeb421ee2582862e50d4e451a7a6d09d88e0612655b30237c6172ce96b371020b2a2558959f5331ce171ee86d9b27562f170bb8839b437ecfb81a73906470e07a23202c620c6a24c43e1ec018210004284a45e1105202595d0e31bb0b096ca258ac4a5f8ce45b94064943d84abb18c9fdff06b3b965728c335c63159075243dbf3549e4a8c2616611f527274b62225e8c73721810af2344c846555ceae3321b596d5d7b51ee6ac06e186c286aa1208115e9670e7da60041a0b15eb659227d2976344d8af3346c7513b72c5db10a746f740a190153f9611d305a35e25c054bee806950dc4ed951a1b0c005888ff55a74ef3874d4e7954dc9a056735dd33d7876dff45ae961126636327e5b0f277b6ae8264c44d6a22823edccc463fc0e82759ef8cc79ab3e1405ec488841358f6363d903010b1419a53212da693a18694d50de348727d9aae46aeef1bf68271368180d319a13b7e4f43297460f571505670766544d050e4f5565e0b0066af1c30c455e55bb14b7df76395f6a05624686684746c3772314800c70f8faef12e614691b6f6789312265c946dd533876d801b735058b1d706640605af6324d4fce98b178d708037bb495e74047ddc025a3e8ee308f31887e930582725d7185093dd39c3510b9c275d0dbbc7e5fedea112c520179c5e4fa626eb7ff045c32a46ed471fd3ba8e98c3ea601b80335ff040f7cdaf27a5f19d90e6946b7371e6e8531f385e806c0b121055fbeb63c144971045b1c4c297f3bbc2fe790516125e960304d24c2746056e969c580d269cda2a67b612df76983a8b9028c8e46688f54e54aa6a0764db5324c13e5a7d23c58785219292cc91a1902c01b2db0146ecbc41f2bf4c74e6cbc363c674913dd2e366e8500e39d8642bcaddc5feb4cae39de3bf262f8a9c20cffc58a612ead534f2cad4175409a974d7d98d514638e8f7e52719012c19c467e84ba6013e4e05966b0b609185dbde16c12504057a0042f4dabfbb631d971b91e6ab7c8452e98481900ddf85190be8829995145561ce05e77c0bfd52b957d1015e07cfb62633f1a1b7481514a641723298764fd4c3914820f7e0e14441ec0e712f48fc84b9696ae3a8ded55319927840f2e61a4149505a179097bba7090b5841660e3cc69028e8e2f5d1e72008a543115afc38104907ef31a46e4d9512de3435a32c4fa5f690d005f4ad2c75de0567c31e6171a7444fd283c5f0168415a267957aa3eca64b0eafd4f1e6d107642191f45bd5e6a75eb2e3e5868651b1300f4891f705a0363c000da4779c1797abdbd6a158db3335f2a878b410f42a94d4433d41a89f7c81c4cfa223b33b40c288b4c055569abbc0b86d762733d418a569420cc343e91f3263bbce44f2a17e71fcff6a70c09a6a66683d0d1329395c90d72e7143ebe87c01ad3761041a9fd490e81fe492e6399e22bcb83751ec9084f43a218a9097994010aadc50a64d8d2ce506b157156cf35f971cd48625b1f844f0803aa4d46b756253b923ebd069808ee4de20acf66ae89ed75ab83d2550f8016153920f83e2b74745cd605d71636bf402585990d4bd3b82d75fc45ea1563fd83682afc0d152304f413cdce502710d6930485784226c3db6100f713714fabe87747ef01dc13a8f3c827f7f6b651c9dd8b5c9816ee1a73818f7ec60c0741ade2e453eb29153a6a3b7c780353205538a1622535416841f693b74139f234401201be5ee1abf24d618adb6d15c00977fbf8ed031ef9c6073717fd6b1600457a8a7b947c1b58260133194357a185a7546321da00b005384abed7586b64f9734595e1fb41901aaf0c214f4d3ae50d392a7631577c363fe460b3ec502098456b07e650120fbe232e57f9b12649252eb50694f69978c9b23179536e0f0aa90e1c2e1c9f6355efa5b13f96f42c09627cf6749dfdce2faf133f71a6a9fc54d1cb0b083cda944a2e02b72239c8541e895b471d72847f7be5c70b5db0ed601243ee675cf526cc361686f529d86c3e2dc36b1f3aee6d4b4ae1227e7478eeaf7bc065e064de5c842ed7644e151f37be58e04b0d13591266527635dc705815422ea4e2f85cc1767c1a0a818b3565f5ed5bc4cce7411c42ac0b97b397389632cb4e25fde65432ed771a91ab4d1e7f7ae548e91338292a244e6802fa57782481cb448bcf6a56ef4664374b4c8719f6464414a316724a9d8a843784caac7109c17535c3ef3c72e2f6711294bb087a32ff4d480dad5d68370fc95f3c4f896ed50bc41537674d2d86e24d174ee5cb3bfd3b782719191a00d041b64239a0085fa2fffd56907614160035561d0514d44749cb4579b055400e45c9ab1ee7eac243b86aa67b699c2e3ea375ca7e16cc1e7746674e1c2d567821871d3246d0f054611dfbec778cf4da3131fb8132a9e53c3add35110367f65e124354ab4609a31e487575785c0e9e1d2541a90b61db57c06d4b4cfb247715d473d711fb6f08d6a71cb3146274c7ec5c0c6b9ed620246159560ec37c2addecbc3358d2d014a25e743c75fb4742e73cc34ba0756664b6936f3cb2e9f7341fb24566bdda27541c6db411f45ab8637bc763456d320f15221bfa576228dc33c78c4e08ece39d3dffec51048385b73921a5635d4da714018d300f5c5b75063ced2be645d3b0ca3c30711a4bb301b006d955590d52b1e43fba2a4f6f327b0a6c0456b0640938dd6b1b7b9137323a9d08f3598b01d0b7d103325ba7390058831cf1b66c3e302969276f94075a27095834edc263007aafd876c6a29c15fde2094d777a1f48d2288b342055a277e1702d77285c454caf88f130858417695295927948ef624613a426272f4d884e92d4b46fe5f6ef1838ac9215fe737e05f0993d26e63d6e7ac34f4544dbe02111541d0c16f5dd7b57e6453e68b65a29687abd6178f6452050532c4318f9060b2763d54809162941136452cf45a5f0401f39c599016f6101110c01f066d111ad1185a9cd184f4acd20284f200d5545025172be5d4e4b7f565c94b8730974c9437acd228d3c0f012217674cf92457c9c968e9b45a6e64478d0415a3225f33a4fa35faa10a3f7f65736e36ca7a0201c88a62b925143d433ae35ad1f70c1f6061450a99d61e60b895e901f6b68c3961c481053dc5223dd1866e7b879d295249162c1310810a2a1e99ba59dc173b4265c4bd61cfc4b2363f6e3f6f18182513af9efd33be610b6631e470270b3883345cf8d41e2bc4fb032f11cc6a573f2d2bb6ae621bf222542d211e270b48b8be2afd83f87a524b87616667275a16a0e5328a4855362c963f64bb9c680b87342805fff5846f16860348da3f4d7eba8f6452fac32d683ce328076f96451f9c80141996ca2b7500b2724740baf767d3943e613f004161811f232eb2143f3851742435e5f52277a5472e243909d0767ad05b4e47923f5d9372093925ab250a26061a6a668249535c91421cf9751457b0d7ee52b656f55371a9d852f4779d1a6d156070b1ecea0c7c9ca956e98b5c42178b206da746be2c24533c133224482bf8f69979b29fee34d88d7254b34959532d2ddb16aca8224388d072361de10b22f79dad78b809071d6d8f9c60baaff77dc735ad2b2d2e3d2d429ed45783623a4be949ba516196a701263e592c0726054e11919133df17c52eb40fa906cb0f3020390336465927683db74281346b8e740bd61b456818598d356838a83da8221d2b7d231804f99a3e5e82d8b81e727b193e1817fb2ad5fb2734a2e8fe3517c9ae3211abc07ed499eb38ea17c44535b7c402b094a44cb09c53263bf2ed0d81edd24d4a506d111345e86939adfd5608cd620fe07b371784cd3b388310a03205ac490dc1a186743822ae75b9db976149125c42880b2458aa48d25d6adba1747383825734aace6573a77b678f7ffa549c88e079f8bf0358719af36c843b923c9dbb2c4cc519e0662a05e24211558e4eb0a958218e6ac463ee206448ac65325eb09a543b1974d6470b856c567f2f6c0c4928b429eff9502b8d9b0a3cf61a472f884ef8618467ae0aae65be751e321f48d4df761d490ad91328264844116c0819a026e644a1f5d427fedea85a4124ef7cabd9274586a24b3500db5d650b57c4593719962cdc4df272dc4f2b71022a1b5cedd8b5733f0447215badbc03447ed67aa0bb5146e194d23111e1b94d9b2e8a20061b1e6938dcfe47d649334fe497fe02a7dd7c76a418d55e4188bf288c2aff21af469003c2bf3c39b599a86bb7b45b758593f47523fd67735040770502be14705f361541fda07a01e6206771d64dfc4b2a9e575bc11d10355929ec4ecacd313c0ae83e5f88cc43774af2a000a11a7b573e57c61d4e735f5fe1d714142abf553e752a9b141df12715e6a3074d84e4f43e96abd020f7a34f4f0f6d0f227b5b2d4ce699aa7046f4a94aceabd31db80b77389052ce2cb6ef40151f697d3f11738f0b34d7924e0c80f6274d0c687ac306623f654c34512aaf66531275a541ee75c71a2e90310394991d0ec627f13b0a919165a1338e5e23de8a5ffee4b63d2554a93f74fbe700593aff7c3649fd4453e4b104d84b052da8ae5f7c2779cd3397b9cf4769b0e452d7b72f4f386ced07fb4bbe24e8cf674f77e751236d63653955a693421a357a75065bb059fabff30a889d521a18419b2583e4c95ef8cb4c556ee91839a32a790bb7c52b6e34effa1a2c6b041f15484b2d8499531c887751747c0555682aada8345353786eeecdfe66e06b7b742da51222b4a8f4327153d618db30dc1f6d92a51f4f57a7783e91a305f78d0550b981c37ab5c25f1086a21d358d6ac71002ce2966c6fc04139767422d6f43530ba82e5869ff3dc114fe66e1726cca154461f52c22aa98d06ce778fe7891080b6633282f67a05ce96a40cf6a34ce219546b4eb606269c33a1340f00676c110dd6ed8908118a832f1537acfd551a4f16147e8278252a5729a26b286381965477b0279ae53718ca42e52bec9dc287addd73a06fb684f7723a33fdc90b278bd6e117d36663e6ab5efd649778a6d411a85cb3fa242a72b4dd07846dc11e74e9aed2a4885bb970317dc890eba974e4516259b19f6baff31341b82465754f642aebe727853cb6459911a583ad2c03b4d7dee715a72aca56a43e18160a18af82d26968b0319fbf170b7f7411f0550b63f4416ab52e0304f5d29b44c5b95fd3b670b80a93517f3d12f39ef2b1a5704947aa607d4641a1fc17df772237797428b74b6ca78302eae1f1c5ec22b0987fe7c6052e9117689e3af009c2d7d1df613763c14a6d647f3b2a4459c91572ca5dc73749f65d04ba57dea287012895e330b6b0d84a2011adc7dfc57e791ae518c491657cacbcf675d06450ead94f217fd12573ce24f9869b9bc783d6cba8c326626bb280f24c465312bc824cf406c2071b93f5246f9fc244cb9665431d1d9160c56972cb6ea3c5f8875721328410419b973844ca9f7cc5707c5aa0a70994a6dff663a4d132ff67595a3043c2835dd3cc9a47e484820dd263cb0a46da79e32380f5736202c24690f4f6fbd3541d6c02708bd47013add2a3b25b22f33cb04ac70cb4c6d052dc5c804474cc3768c9e913323f9c04bad51c10ae5ce8f35be4af81cc2190e6de3f9c4668f90ff3866ed745cd25d31524708f445e7457661f1f1c86c64a9b07a962baa59a8f34d663da13e6245af5a3f8e15bb4509dd8c756eac33799628ca77e907962137ec293be049d94224462f1ce603d962cd7c353cc5e6642f5ddf7b6e35b90a2b6f6b9c1e41ccb1481e0de449879de5322784691f93ee877493c64459712b5c536730db6562e2d00ca210c4703376a165973787038cd4d477c2310c3cde63bf798121f75b84685376c0776d61c9e4c85ae0dd7c529667070aaedee901ed756a132eeeb866c1a0f472fdf1fc1aa62f8945dbd0676ce3e32f58b0b5b43f44f45e7e58069801252e2319ec91544763fe422685827104f6d3157c4cd4de12075e9903fbe5e20bd05990285947ee387ec740746523ca5c900145363071ac21fef8095ab9daa32c7854621b95eedf78878e5225fa16252d84b9245606b22a6e682da4214d81fc38f12af5380a2a0b770566234d4d0aae3528838d4eec613537aa59fe622502510306bdd207cba02e796a9d1447dfd3e415ea500927a9303928c266a3633d4885186348443e9c838e122bd36f384d058b1797a8ad48b5d0e8203ce5965df96a8405b4d1c752a9e7d23c3efbd51835fe35295172905a87cd7018619fba22d14615098b043e55ed8c7e3eeec42511f966f06769373d0cb66da2296106f53685a2bf19e13ec307d245ed1e26d97a4baecace4fed267f61c990074e151d034b2ffd4b4d0badc53b0bfe7079b3da4912c5a789335ef7162d11106a66b23e1214ab118856ab82a15de4bc5633bc684d0707d6c978801a7b71ae3ed95451ac3e1f9eef4c1351eb4e3855b8e9347b080206f6245a23dd6f4e530706c84eb03d5e6e333b9f149de777403731c559d785112e92c1d54e3005a60e05e27b36dcbf9851b59a071e0538630d2421245bee88c02664727b7c59950c517f94f44abce59a5c2081af1ae19293047d7e0b0d5de9047c8cd55c7339c3176ea413d812d7f7e91c5d380d54b15de82f7dc11b36ce7e3446b8ca5e455ec56c50c39552457d560e79594e036d5e421e3d579ce34bddc75c7e65c68e192232ad3779e75759f32c8b791d119372e5bed55119e4ad2a4c64d37bf8d19d7595a3f05b91f99b64a375552f1c15ed59cbb461758d59f2371a5c803e8d821b10294afe6be9ded816aaa93043464789365c24c43d1b1613227a3027527dbe504e854c526682e1f6753a63d23f74d72a6b27ed187541e6d81b41c0045b44d9110985d9e6038132e017101a3b4cb4a8303ae598a060b75d6414cb60f87dc9f07a6063eb3d57b6f0163d7120a04cd9ce135bfbb2f5521de7106bd218685abaa5052d4a96ef474fb88851a054411da0e0f25844da5956fecb1323cd9c7553d04b347de643071c26320147b2c2dd707759e9128a544c3877361577cc35064b5e94a65b58a05b7afb2e873479341e728329200f1611b22827cf9b12e109000694e66b34d53fbf0961b07a5e0d2e52453e50931b3bb99611659bf353a5e3bd179077ee19bc70132c089a61435d58e61182de6b5ec302eb210831c8003db3f247e058147beb03417a19ef3e60acf2c647b58ee64533c43400fee67d47aa74295526928860d0c2865ee3674e117ed6707b18e79e40ca41ab375129014a2d2a073263f3b93c73546b2a96084732bd96a008d2b1f179c1ccb14119a11b254994aa21294df91101a2dd5fd0dd50752606d35041183252a93067066f5957204c68c824adb4a11e4e5f5a6b1b3e6b708cd6700146800a669320f136052b7135ff392466517782751d58e76c358389556fb6af539c9f331cae9aa629209b636390fd0f26211cb556c388e8568662941f03ae672eace33438f61acc277f8686436855cb2d1b3be13b5f9b8a26c28b7d63a85ede73223b677bd7f620637570a877da5cac29bbad3f260799c13d362393406ce2a910dadccd04da72aa2f22274f2ec54b59087d7acc59763481309d82d73b19f5c862114b977e19fbdb05613ca02c479bc96cfddce9384753c27c0b599d0df72f154b1d6f1d2f9011fd7ad4c0c15302f4264a310261347075a5011d7afe21c171e92994d3884aba6a10348940693eee216517bfb74a1a970675029e25de0f16f6c3091cc5497aec223718dd0e301f15227571c5c90876773ad32a03271d2bd6600d7161d67c5841ab796f91b70413365ef655ca7b5872ac96c37dfe34fd37bc0fd83b1bb41d44e56f4934039b4a38588c360242fabf1a032fe370752dab15be941a18f7c09b0e40d4937cf2e4752fdd16436cac4de16a574e976316fb0b4a853005404a53a94fdf4145785efba1323a76981272878223ca65f717f4e0e94a5ca4cb3a9ce0915579ca75580fc0706f998eb30e26e9333e2b62073c13925e117c5331534b53fc583ee04c6d8cb0365a2de5f835b68add5145921c771a6d1e7adccbf57203f1cf06f1a44958ccfbe13d01cda25d0958434678a5e40bec7da36e95ca0068e7be0e5cf2f23f42e0b1573970b51f69eaadfd30fe6a817a9493f76b084b077b5b53a75b4b413e2346fa9b7bb88bed179fe8886b2ae8692b869fa541fbb9037024cc6627ad037f52ddb42b59f02c592445bbe127231889119163e41afa64f21b6149ce41d7ad3160f49ac1645e5ca01b2b2f8f3c033a2c2b013677612d5f6574b8dfbc61a417502eaf882d6a47d77c437f2fc3656836101a8648974b61db666ef7f09762d614162669455b422b8fc3507aec39558ee1773beb537f201e13f4558defa14205ef9757fc09e7335b6b896cac84c32e2d67f65f1fad8a4ed9b86d4a0fb423241768df51aec51e35ba6131141c8ed24d2774a96c27afa12b7d1546750b03af5b1f0f3f722210e52a9c4a534f4389ff2ba8d0b1629773b37221426a3c82688e14c8e04361e801ef3748aacc3c5df12e02804d411dfff12f4266d981692f39ca344984642472b29e77856c7b7b8eb66e1e80056a078064eb699911043e7902840f26c8e135181b076b2a37c96a6d4a1b585091d1006c0e96293791534dad97e36ed8a7486282a05a5fc190f07c4e3f8f658acffe6b4b8f54452573a035d23e5f086b31af5ca5d53b48f6ecb52f98e26871b9831b3ea892f32a56d93f3bee7041747dbbd318bd82074202500511a3f68a074ab9e77220da300ba0a041184846c449ea8c7c0bcd61ec6baa45784d8a567678c8a3d5455612ef6dea1e56617184266c7ce91e7a6e6f33305ac5df211db6e925645a3e60ecb2fa314011be68cebcc12fb6d61302cd0ef21e3a98df67cdee6020c8dea93c94b41e44b155925b77dcc42fd166d31813c15c52646f8d3588dc1413cc85cf44d9646f3318b9a54595532434b166dc05343e1572ffc57c6f1e1299271f84350f2dddd37cbc0ff3573901f63507e36e51310ba3113fd53a7bcf74824422c5ad0a8c6a06512d74c77cc1b9dd02d0cc2e213f98a87db578764533840b13b610ed6b9614a64e47dab91f59e2d1304609c1016560ed798f16cb2ff950a01eead1824bab56234da7063d172b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a028c172a88bb9e4788caf21e6b54ea6652c4c16d2d660a7d9d471d71350b9828a887691d9c4aeb32f4d5612b7c403a0caa2b0017decde16cf1bf725a5cff41217648f919e03b9e701ea9830d314b412c81fd9a3400d36c21bc24f06be85f7a11d8306e472817aa1714a8c351e820c56d7d14324018cce21c76edb4647addc64ca8d43b1060b4f8035dd0d03211226e23f4134f006d4f1908d78846666ce1f43ce245e93cb4c6f66d0b8829668e55a016243efd572a38d35c6cb9784c6e73df18b5bb553e38823c0c91b25302ba4cf80c0b3e83535223f17ebd407c7ecb81052e2279ce2fe38e136f46b3221f52f20f20334d6f662149a2700a22b9643b9efc397020d200a9e8f6634c26ae04a816a242d5830c6ba648922d6a48706768710c5a2b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a468c8a6c3076ad1ad521634683abe3671d67c432fb1b1613f2357b33005a240b40c6a7197dacc80088e7bd2080159554010fa74cff82e0045c8ee0397b39b76ccdc4bc7635ca0e60c0eee159ce2c5f7da0f99a3e6e2c9423af9e101b9a426f6bb154090cced17c08fcedd34109817c21bc495d3785681c247730675a430e092073f341014c6487672c5bad28dc39174f429cb6689248c533f20bb84781034751e27d165722c2ba3c8925a1340584a606f0b982573d8dcc622abf7e177b49a51b8890420eec7af9756e1df13c3ca03726f5a2894767bc814888463147aadf6f67fcf8fd155ab5e81f624c330dfb169b1c20ddf95533c0f04d5a5a287d7f3c943a59e2d1304609c1016560ed798f16cb2ff950a01eead1824bab56234da7063d172b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a1ca99d44da19a17450b1514a4c5c674b5ad6af7aa0aa5f06dd7c773401e56a2e5bfbbd5f8b620749330a0a5989949c3e2f9f0b63233dcc186fc47866c50d7c58854e312934a3d87a6b902a27a4c83b0de7d5f3331da0542075c649456ab46a75e90d293cf2299f631effee25b8cc3d027a526a6d96c0a711b830c52f2440d20d64b0c567f261142045f0b00941b98a10cfe0f641a450d00467a03c5aa3109528a70b5d2047fd5b05b0dc451185d3f72c589263108eb91532b1388762e574041f80cbda2afd5edb4e755a8847461e023592e96c7cc957206c87a2c3575a71397de393a07996a1533028f2ac3b8ed45e4e32213c7c54ded31e6e3475034a18780bc33c1944fbc4d050ccbdc74061219e122e71f968dc696555de05ca6f7b22264294b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c15042aa1a9e70fef00e66bbc88d6e89743d2b9592c81153d32622368c3264a26c240e93a9447d8606c420e327dd58802cd93ae89b1b6fbe648f30d89cea11ab816936a4634755cdbc8157043f55643d2e84551d0aeb2d3fed855d8857cf7cd184bd37cebcc12fb6d61302cd0ef21e3a98df67cdee6020c8dea93c94b41e44b155925b77dcc42fd166d31813c15c52646f8d3588dc1413cc85cf44d9646f3318b9a54595532434b166dc05343e1572ffc57c6f1e1299271f84350f2dddd37cbc0ff3573901f63507e36e51310ba3113fd53a7bcf74824422c5ad0a8c6a06512d74c77cc1b9dd02d0cc2e213f98a87db578764533840b13b610ed6b9614a64e47dab91f59e2d1304609c1016560ed798f16cb2ff950a01eead1824bab56234da7063d172b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a15e0c54e356c5607d5d4e61b3c333e0467c6940bc60a201d555e4c391e369e4d10e2e97c1d56c968992b645fd1ef88606ea49332a56fb7299b352b203b409f5d553ac8301e16d30da5435039b550100a72e1a62c7847e8529c99a16525890e56e9df7849c088d118ba8e073894505f7d07414b6893038816238df721e5c1cd7a3b0c076713b3952b85baa5164abd0c028617614d406d245150b7845a452c927390fb1d691732bb0bfbf4696bb4779e347d05fb047aca825160988c08fe104d7551da4a0acd08880e20e16166c993d07e178e4404bdcd077def7d5f707c4fd47837698872f483e13e61ebda61272bc64673b6117d8542d65b91d38359323a85762032d742fdde575d9470da7e02a4bf5cc29eac51dd6a5b432255aa2526ab800194b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c15042e167547159518d4cb76d4e4a4476932c904b17023c16851470641115d3a1325363484d5fb4010112e0060b4ee691102df097df394062cc7aa7a3235bc3672c62bbf9e50d1bb46a2b9af2b33fb32d5602fd34df02ff5fad4ee2810f24cd37916a45888d46d177967c55d3382b0b97e41294a966077328e740869b514eb05fee6e37b362100bba4477b2731d717d171c25288cab7969e0816be70fd75a7fc4ed6f4ef8d071ef28b63b69550f49393f81350219763a941b656f3d40ab0cfe2749578465f9692320fe18e3f1182f18b5d11dd3f4415c112b084e8209423dc60a0529596c601bc4b3a07c7b75ad43db8c8a17a2dcf61f158785243d16c42a7aef9d0ec33c1944fbc4d050ccbdc74061219e122e71f968dc696555de05ca6f7b22264294b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c15042adc4f1712509ab371791e11c6fa4d81b2e4ed42667d875771aa48f24e4ec7919d988b1269148e83ab47136124ca49d3180ac826403cc3716a314b535c2c26056e63c9132aff7985ad21b2327da26f330af79ad72004dd3302c5f3b031226f755dc710159fec23a5b686a2d7c642b2d5146158021d29ff879abf4e15afa048608a8d43b1060b4f8035dd0d03211226e23f4134f006d4f1908d78846666ce1f43ce245e93cb4c6f66d0b8829668e55a016243efd572a38d35c6cb9784c6e73df18b5bb553e38823c0c91b25302ba4cf80c0b3e83535223f17ebd407c7ecb81052e2279ce2fe38e136f46b3221f52f20f20334d6f662149a2700a22b9643b9efc397020d200a9e8f6634c26ae04a816a242d5830c6ba648922d6a48706768710c5a2b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a7ec3925a459ae6779eef605935d5bb23d9b56a704ebaf86e05cc540f3d8a3d2f8c8bdd782827464359ec6841e66ff17ef667207410608c3521e9282a97ea0a793e6eac5c4ccf4502418de10e40a75933595d52220758fe2fab738f59ffd4d901942fa746b3b1a633de8a556d3a601d77572c593ddffd27698d1d3d1cc9a1927eadf127694bba987984d72328ee86ba2bc7ca083af155ae2b9bbe6b708b964b4cd6d7901d0606b63302c3a150071693117ac8c3400db93025c000de10a126cc1eea679f0ad40d2f0a2d16df3d77f4a606f63c8915e7dce616952dc834b73e29412279ce2fe38e136f46b3221f52f20f20334d6f662149a2700a22b9643b9efc397020d200a9e8f6634c26ae04a816a242d5830c6ba648922d6a48706768710c5a2b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a1281333bc697850a417ccf747f5f3a73b4796f39af9dd9094ffd76140753294093a9447d8606c420e327dd58802cd93ae89b1b6fbe648f30d89cea11ab816936a4634755cdbc8157043f55643d2e84551d0aeb2d3fed855d8857cf7cd184bd37cebcc12fb6d61302cd0ef21e3a98df67cdee6020c8dea93c94b41e44b155925b77dcc42fd166d31813c15c52646f8d3588dc1413cc85cf44d9646f3318b9a54595532434b166dc05343e1572ffc57c6f1e1299271f84350f2dddd37cbc0ff3573901f63507e36e51310ba3113fd53a7bcf74824422c5ad0a8c6a06512d74c77cc1b9dd02d0cc2e213f98a87db578764533840b13b610ed6b9614a64e47dab91f59e2d1304609c1016560ed798f16cb2ff950a01eead1824bab56234da7063d172b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a296939128bab8e180bc8b774b37f2248790abb72625b5f009b427e4f26fd82445fe2841e43d71f0e6d0af6621356c95c597264786ac7a23a74051b34964f0024cdc4bc7635ca0e60c0eee159ce2c5f7da0f99a3e6e2c9423af9e101b9a426f6bb154090cced17c08fcedd34109817c21bc495d3785681c247730675a430e092073f341014c6487672c5bad28dc39174f429cb6689248c533f20bb84781034751e27d165722c2ba3c8925a1340584a606f0b982573d8dcc622abf7e177b49a51b8890420eec7af9756e1df13c3ca03726f5a2894767bc814888463147aadf6f67fcf8fd155ab5e81f624c330dfb169b1c20ddf95533c0f04d5a5a287d7f3c943a59e2d1304609c1016560ed798f16cb2ff950a01eead1824bab56234da7063d172b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a97e22d5c6db6b9208cdfe16d88b5ea3daa74f03af637ef318f074c28118fd5116c53be479bedc26ba4d1f3222181ce00dab21d35b80e630228da8b4a47cdb63437c8f74f73775d58bcbd253041256d2550af0d4fe603572b5960b64c188be96848487928f1490b4c3af356375d59cb6739f8150a17abae2f729146737d978272145a20407eeb5643253e5d121823831e8ad43b2d4ca3dc523983e96064519809f84cf45fb6ebe73898cc8c0b2a26f3296032fb06d3757f4f946b302448a5fb1ce9fb34028030272c0cba567bb91d6c1e959a9355367bbd7165c0180d0005bf18fdf9a57d7052e226300e0443aa70d371e4b9fc1057dc5a155500843c0b8703287020d200a9e8f6634c26ae04a816a242d5830c6ba648922d6a48706768710c5a2b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a4c2f317c5fa2687bb6407420ae65fc0da1cf4d2cca6866055e824b4d5ceec04e83763954f6a59e47486b617ed8a8f040c5b6905bcdacc50c517cb46a07e51d0d866bf82e91736006872f8b04a261794dcb31a23ec4754d132109ad6779df5b5d2038b3428ff4153ed4a25024e495c95e89bc935381a5917a8ec4ba69737bfc4b0779e57a12a61c0c8116da5919e274218de906742d015a735e2bc756522f59178a15df62e46d3623f44df17080c9a57c2e91f24b5d33c4519f4b4f3755471f5418c9ca2bd2fd5772bbbe2f366e514a49df296e53ac104b061d01391a926fb402596c601bc4b3a07c7b75ad43db8c8a17a2dcf61f158785243d16c42a7aef9d0ec33c1944fbc4d050ccbdc74061219e122e71f968dc696555de05ca6f7b22264294b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c150429d29e73ea9e31866b87f991e1a679865a2f7784e22a8865d8d6aec67bbde76617537793d25818721230059425147fb7e80a1f306a1d113746775ec37bdbc1e5db520e46d3bdd9443a90a7f30026b3b1a8a5068431afba61663585517b247dc6bb070f71b3471c5580acd166aa0f31b5e7869e10277ec0635367d1c391516524b5bc0c6262e0f3730b00f42104c8a4b2d007ad0443b9e5919e6526d7b75e2eb62ee86572ef0975706c9fbd741637f7252bc21921f3486871f9eca1e5aa68d3c5b80cbda2afd5edb4e755a8847461e023592e96c7cc957206c87a2c3575a71397de393a07996a1533028f2ac3b8ed45e4e32213c7c54ded31e6e3475034a18780bc33c1944fbc4d050ccbdc74061219e122e71f968dc696555de05ca6f7b22264294b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c15042e2c2d41ce3ef387475a8167bed19ac46131e4d4b7add993935ffac048f2d033cdafa2c5771ecc35e5ff721076a524e0a3754fe11a761c806ce9ea024b7ceb06c27ebf725a4b2bd0d5c49b22815b182129f264e66cf8cfb34f9c725167ef7dc1eb5247851d9cc8a345f25221a5929e13ccdae460dab67dd0040d4853e5fd3e60cca50df108a63b619eb16320a97af2539e4e3553ded83384a84c92b424f0a691d92b9f45f69138241dc220c62ae48813dc7b59d0eb0d57b73f828760553fa4f3ae9fb34028030272c0cba567bb91d6c1e959a9355367bbd7165c0180d0005bf18fdf9a57d7052e226300e0443aa70d371e4b9fc1057dc5a155500843c0b8703287020d200a9e8f6634c26ae04a816a242d5830c6ba648922d6a48706768710c5a2b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a2d24831237bd5c4ea815641e40bbe442365860799e1e510205fdab4122f39b0bc338093cd4c6c4015c6034660018cc64a833ac12b7d9917cd241a2068293a74ef3721b42ec7a8936ef876376cc0c402fb4818419834867439e8c2f4b5f5c746fcc62342ca039226b3b54631d15f0695d95176704d30b123d901b5822a55cc73e171fc959591f316af4cf1923fd55bf1066032a027670b24f18fe32212159ed5b92b9f45f69138241dc220c62ae48813dc7b59d0eb0d57b73f828760553fa4f3ae9fb34028030272c0cba567bb91d6c1e959a9355367bbd7165c0180d0005bf18fdf9a57d7052e226300e0443aa70d371e4b9fc1057dc5a155500843c0b8703287020d200a9e8f6634c26ae04a816a242d5830c6ba648922d6a48706768710c5a2b30691d512f114ec690db001a140f09951c7c264b003727b5444c61b3156e5a0db2526cb984440884871a49f8d6ec5b01a22d17078086697a5685092a80f52be8e3092d3921a24c7bac8f3451129c04468509665997ab7e7ce08017bb248549438509792772ea452edee42e7c6085379984c83692f995247056f827b523c00b4dbfd0061cf01c686a905a6eeb1c057e942ce132e772ea08d3e9fa6e40a79d771755d942eff84e152c91ab3ec647403eac6c380b1a6ee44b8844244712d5906cddd66e302710ca31defad12799b43637a709d24b173ddb5542f3ac121259e24090c951374ce750602a9458292c352310ba14bd4d770fde747320452c9949b30ce393a07996a1533028f2ac3b8ed45e4e32213c7c54ded31e6e3475034a18780bc33c1944fbc4d050ccbdc74061219e122e71f968dc696555de05ca6f7b22264294b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c1504231fcac3c2d266d3fd331452d32fba51331734d35ce82112dd54a6a03b19c6a1658bf9679968fe7401e6c2a2ddf21ca1101c7d90a4bdf61236b13fa0670ad2a50f469510cf737a44e5065da274f27ec5e25a8db258e729c25ba8643362a4c740ad26fa604a1511c09116329487ebb8f3fafbd7076bfd67b59fe1dea43848db13ce615fb48d62c4641d3467b7d0ebcff1f38fdd41a89fe120a47a8cc305513e676e97df05a358fe00b4fab643ffa19704a4a37c66e24d80479e9305626fcc2b4599b1d202a0aeacd1d8b78134aedd5b505a0dba965388b955af0c4cf0b00c0dd28e38b0747f29f3d5109b9a910c9bf413b9b69073b8c14f53aee8f6e224e064d1c2032d742fdde575d9470da7e02a4bf5cc29eac51dd6a5b432255aa2526ab800194b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c15042a4684d73257bc73381419d55e9de024648c1f0644a00f941d52390199aaec61e38d6fd468c477a391789dd2a730a37063491e476c8ef3d6ce932f00ca861563925f2a03ee50a47244d763b7ac9e1377c6b2e5f1b12ee4c55cb26e807e719e21f078ab660dd8eec130a2aa26bced00e54bfab271f6f1666044092314c5e05c051cd6ac5698a90ef144f22d5303d64a26cdb2aa44b6a832b4c2e3deb70aafd347690fb1d691732bb0bfbf4696bb4779e347d05fb047aca825160988c08fe104d7551da4a0acd08880e20e16166c993d07e178e4404bdcd077def7d5f707c4fd47837698872f483e13e61ebda61272bc64673b6117d8542d65b91d38359323a85762032d742fdde575d9470da7e02a4bf5cc29eac51dd6a5b432255aa2526ab800194b40d5a470cde3576c39a6350e64e4bb0042c0ea142034ed8fb7e15c6c150421b43724ef22f0c47ed64172f4c560357399d9a1aa8735855c3ff497ef0611a2e77cc5337ff4af454a5a8b5345fd35f188cdcb0188c588e32551fb93b062cbf5d458d217094ebfe797b7f183fec5177647906cc2fec92293457e8324ea33b616c12bcb83e125b575f73773a47d175872656088b5b5d7c110aa8d7e6373a97f21008519738c23f38198834b96610b05f35250b914c985b76760253527d021aa9436bf4a138e650dd6cd54b84025e7aed03f856c85f95c2ea6eb836f72bbb12b948d46f636027ab3920193e3543643104622aa8ba22bfca765ea5d8794a410e351a04b9247bb5caf44ee3c2b2762d82861bcfa7ce0fbfd273785149e33d6a1b16575ef6fa444289d00544422849448dc077fcc0e5741c3f710ca1a5b0054233061db43b2e35c6e5082e7205ff39bd7c6d3e30aa8a189623ef2a2a00983c2165ab4d8acc630730af9601046ff24430b57324a4ca0264ec12023f579b8b5d3cada428bec30e10d0d233493b67f36db0f9e74365e4964f152231780ce31e5fac7bfd2870748c78e735105a6e0baa748e057e14223b613a6f23627176b12529bb5c011565c5b326a0af935b8d252f2fb833b26e03b66911d9113716945ab937cdb52a14b2165b2672679e57b91c153a2c689f6c31147a3ecc171f795bfe962366dfc85c0a91f2602a39ea56a697543830f3720eee1a6b3a8248f60ec98d4e13979adb069930f2622965b937208c8c5b94e39160c99b2e57cb799865c32bd777a715011db8209309ecfa3f6329f7ff3573c0757ab5313a525cdd0f4441bfbf487d94162a57200a521ec5793ebcd8622341c8b82c69331049e008140280edea1e03f9bc60785ef27a52db6c7712faf153b3cac63fc4201b651bf54d52dee0835a5d745a18b915833be50723104c22ee483908c705698afe696d693b0824575434bd0d2d71b5b4317cf1dbcb2d25f1c424b9b87a67b2787c494c981a0997c7cd014001562346237275afffbd3306b3c833a6ce53743ede65715b65fa781bb72667c67dd61b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000321aac416be10d5cb4ebae2eab98601366cbcb77bad73d2fa06d7e1991580854d2abb06803491d5a7d405358b8ed5879a25328779453c20d55ddd45dca723d41d87eeb755763805cfa09fb57437a22077ef24f04cd53345120741a2be3452a79ed1ab6176e15b844cff5e54eba25d06d906d813461471827676fd7365e601b6f18bff82e9509cb2b219afa3bde2a7c35d2bb395c313565245d5e32731d3dd61aec4b8c5c3deea15ba9f4ae07d9196b0f04e9393000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a4ad22f7a29345b4ba13931ffde9c14e9585f1b1012416d25fda77b20250c605e41a34b6303047df7486c2b412c424d8d66b71f62490a096757bb4262b7984f10915a4e82b7396f36dd5c2d682e041d075a977d68d74f5548c3a62e66f22e37db9d796e86d69a09816a391ed642a66d60841a24da22e10d35920f5b83cbe1255f83d67b16651a0c1ad04f6402fa0458f899734b0da6455b7552e2185653e74af3977f56697f8114eb402817d79f8728fdfb236000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e26a341c5a8c013a0a7d61b0b8b9a065aa01c1b77d7884733cf527381a5dd5aaea8e97bae3cdf2b121da86320610f4d3744ac74fd80b9770e15a8540db3e42dfb3b02049bd28f548a737a76ddef5e75a27e931445aca03b9b2d060560a8264c9e41a65ced2a1e4f1b3c364d9d061654149b60685b3bb5419d384730d930574a94223c576700090f155dc118da49cc3a329d9852ddf6c60e6027af7a9c20780f46f715040e6eae7d211b9740a8645b29e09291510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000712fd67114a7783a190d2671f516316cc0c54b6725a7a072eb3aac00579d682846817b2d47b8336077889519e9e93c1c458bfa11f37e5c2367e69e0e91e07760103a332a689b19744825b51f70a13f41226e6146d7675f780faa2d4cfcbb076dea036148e05fbe39857e735754e87a77a115813049e1bd33448ccc2535c835382b7d7e41bf75d9686cdb1874a506c333149d2a1b4cb6ee1d9acf682891e57401576fed02e60ebc1f9b63ff7e2cc2ad4211dedc2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dcf6af545946584b64970a17d5d7e44f2f384a32f14450682320c80fa82bbc6deee6af3e723380238f35132cd692121bdd22411c1b706e489fae002eb2138b6366a6921e1acac66ed15d5d00fabb142a91250a491eadfe6869969455985fad3d9b05aa6ca6304e365cbfaa4bfe2ce663c17d48486b454019264de435b459291c318d09155bf1e36b667ae560a80f0279c5eb32173ec39f157db053247b10da29b5370b5fe6e48a04cd10107bea541951262a3d6d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006bf76a58ede645062df6e753471cad1a9cbd46661956877dec6af4231a7eb57ebe6df54239800d0e51525911d31d3d633c090a13fc6b60487b5ec769941dec447615e63c15124335978392580b088f35c7d5727707617468a67cae467676207c862df409feff981a07ad8158be651c4cb9e0612e065f446e4c71f91700a70761ed791e179c65f61f71391e2aab9550293b4cc82f1dfeef48cb3f09617e88500b5458b858f362385184f27c0e2ca7de17450e884c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e75ab10cfd843327b8461c74663377528c7eb00c04688d3cdc9da853dd4bc30a67c95c70f4f8934b80dcba6cd26c06546de6a3312288d423f4b4462a6f2e76303e306769e0f45f3d09cb201120427e0380146e599279b310e03e180273b4fa76e070d53191f7a4727635481bff8d961e6446a037ccfef82492089c0a4e78d65490fa1527cc003b223f3736562e4e3a4c0c6c3a3a53c947755bf0ba2dd1de393e16f098367453426a9e27b33052eaf05de6e6ce6a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003322fb51b21d0f43ae7bc850f735b04607b5d32bfa84271332a61b1261423f57a1d7f1157f34b36c79ec0a1b9877550208f487324b262433cd42a72405bb417e758ce459f627103ad30e4c380217577b4171c44904d8706eb29fc40bd34a80321cf7ef50247fb8255e9f7762fbfd9433d018696a6c91980af9ad0c1c3bfe4249c507f548481704063ba20f2ff9e254273138f4589c265b382b09286bd7868f6cbbe5b4666bae2e63d18220641728b767ddb14e1a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffb663206f7abb76ec17094deb7c86123564c54ed2a02e34979f427e26d8776fd649d4483d976b269de60e74c08cb62f3fbb1a55566106036284bc4c4eb5d91341141d1ce098c209ddb5972166306778d5bcae34174cf00018cb7e63a50feb4449b6b261b64852217dcb373e4ec0e11d0d0f7c60b3d34615053d4b54b6029a5280823d6904a36d33b41f4c0aee7d780011a82248dc2c57088959de3ccbcfef53f2e590535631d32a70c4235d917bab607799fb2e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae6f29309377fa5276f26341c9f796557b5abd20210c1e04d501975c8845145eb26f267dd937f65aeed28934da1d313c563d5063230a1d1abf05dd50de062e3dcead322af085df3b86346b047660e462ac38ca0aed7c8242efb6df1057021f0a1a407e79bd423c20a49dac6cadfcf66b47b67c1ebe97d305a8a8ef3d3edfbe0648b7fa2563c7866fafd4102e237e913c49393c50599c656bdfbb4a6ac5357c074b0bfb38cf1c6d638d82ed1f41885a27563b0b5300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c1e404762b64a66601c29186e7541180a543b3b570dc7244f32bf134cb8650b8821db7977b7a03fe4f58b01bf88283a93def740ba92a80cc12243291ef45c1d5ce810289ae3c14a19287f5f5d7ea9291c84b5423582191e4ca72b6029f69f63b448b636724d801792e4000f8a98f0056b7a75248da0a95ebcefdb007078915022d3916dd81d760401755f4e1593bf0c0243d14dcf0aab57d885a5016f074f6dc14df15e95087d2ce69245539993576f0e98b62c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c2e367452c01066687aa8b50283eea0195d6032a69dee317b16d330644514261216ea94899153759b7987a4b7680f4518c2c8c450b10c95fa08b2b77fa3c006dbd136c517e197c08190c3056c46a1d0b8b5eb44ab68b855d6e5614451b0113247d9ea943f72ac207cb49066c4d6f6503e3d4b5481a83500b6f377b07551cbb415249ff776ac48f456004481885d086556d9f5809322bfb24506516711d68eb68bb01c3429b76f01dbc125a23dafc316d3c2829140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa49f41657df32145cb9d41a4abe155742d7434fc9336902dde2394b64e05c63a535720c7539706e956c952d66316733e0dae5573a19c13ab7169e4bdd7b5259b0949b35e309ba71f5d9877cb35d60222becad4ffe15b32112245320ebb53813db487d67dc5d3104e7a3f965183dee612a75100f4301042fe8e51c72d6be667bba1f0d22da7c250129bbb70ee97dec026422ce582336603a71b8eb54933687071f56046b98dd0150a2caee7e16a868377298106500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c62eb7a9aa3f204fbef4129ba15322bcba6cc78da41447448b7674723a18b25cbec9d5452409a364d1dd43b6639e24cd2a7bb59759c2e2564228750d250bb77a23d56538499155a406141088541752804028c01aa42c72f5c871b77bc17807b7563b53e541e7c69faaee82af4b6576b25796170c04a3250b92f5b0e3f5ae50dec5ef822b836ad35e89b3f1bf96b9a117930f23a18decd788e7ab87ad87481679ca5bd5ea69c3167a10b52482dac08591eb491450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000975a72178374db3bdc31e6767ba90f5c518c5c5a56890a40b1a31d17600d473c7a03876f90373f4ed9166c6451465c5b786a657ef725ba076f4fdd4f51025d4c5ebc5a4825db8848b4194064d459233a039acd467c576736dd52bd28f50e4e6bbd0804023b701338e2ee751020706035fd0c2f489ad3f4314be81c0d12d4ef23ade3466d9e0e36693d2bdb39b26e483a8aaa2a00f07e6361f130a963ee829e1e1f8a511611b9a8271009156b395fcc31e4b70b1a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039afec4a6ce8cc7cbb2af535ae9c1c5feeaa9a09b2692b5ad7469d57fd1b9346cc3cd4041fd20826a7d67e113d28552d367cb36094450c0930b82e7d6750fd5da2aa174b3e1f58572cb5bb2039720b3e06b45d6f5709563e98b90f6a5f42181ed4edc562542ac00b743b2e479bb41148cd7beb6124a5ed0775d227039e5fa90f5ec50e030a5e9063af71d70254826d5ebfbef23176c67e3a2abeb21d9835b161b8e46f0c58d25e717764745ed0e67c13f2b54819000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016b2f73bc3087a110cf0db6e87393b4e2b9f160b07741f2df756d82ba9b23f6997ab0753d4351466bf6c7e404559a72a5dcfb54a1607b67aef734b7a5016761e64988f1d52dc32759399735f4ba83713863e72787965c107ad21da0a88ea9b180f87b8507cc9e56ec1b38241455f8326c2fa162ad4892443a15309129cd94f316386ed517914b0688290fd75f6832979f59b987e4693aa24fe93e605977e9f566aed0c029ed19f1c08f85c27202b5f03d50513170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f832e103a783c714ac9aa30f3f5f6b0fd037fc43e6e46205ff8f27656d3d7c633c2a9e7a3eadca26dd462649d22d2325d85ec02c4d4c3a017dabde7a8d6155526afbeb0cff070a7c269f5901390ecf4063e348063a486c2ebf05a92b5221942e7f03186dfc9379228912f420449f6409269a63738fb668384b239960f2971959e802d6337c796906046e9f2928a90120d2ea453c14deb01939d54e424c991f4cd4daf76ce76041533c077562b693ad39f67fde1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000069b343647c4153299827073ab73bbe2e317a95284e359c1cdd79741258a977092bb5715940205c1a110b6023c0622f03bc0d1926edeedb0756bf5745f3d8cd545a06d9682b2f87397a429b25046cc34269c7aa37c1846252e72b4a32b24813772fdd2d470db8ef6d58959741acc3830e23c0896c22a85320323d70617eeb5179dcd0cd6842961f4d8d70bf5132463b6de312791175836f69b2bc9432afd0b27b5f25364f86d57f7aef1b701df3ea3b6f290e2a3c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fba5dc5c6bcd2c0a9cf3770e44c6aa332b02d75924c15f60234be20697d5d61bdefbbc3c0b5b2c6f072c0c15caae5f6ea669ed56abb4e90ff6cd7a5d4c995f45349d4c562af8e94e3038b02a7b755269ad39934a669ac02ec3da5c72071ce32bb0a9bb33dac84c54f368c90523f2ee09a52b8f2797909d160090ef6335958116c77fd13faf2a7f464d82384e6defef645067385f2504d208523c606597873b6a5ba4ea4e7b799c6301592c04d5fdce61748a692800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009ddbd86e997d6a7db590105e200439795c269912523fac2c9f1cb6310eefdf3d70b5ad080a82324b243610433ab2ed76c2816150e32a6327fe32d6199fd6e55f0711a66b15c55e27c048a0573ad2e7355d7b706e2ed5704d60025c241cc9123c5c68fd1771c2674f64e2221b62e51e7c78e951341e8df93c99f6cd3892f82316a137177efad5810473ecb70c81aa6c02dd11e94dcce98c270503ef1b53ed024e756a8155ddd9d46c8da2e53c07fc796d4531af260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c63f351a07e7f437fc3b600486927835c6d4fc728515ab10f4840f30c23cbd1a9f9710048a442d5d07b46e524ecdf57ae6ee584bfa4b42533af78808a1c958007421f05e68ac9b3692f82b71ac4f303d20e80653cae06f4ebd217e1c033f7612fd1f610b0f994a06f6d2026a19267a6b55eb00282c08e516820fe628920acd717e7d380688cd2b323ed1ee65e2cc636a14e2a86675339b43d1aad326c8775330173dc23c4b8df105654743139ce70b566802a37600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c228a10d11de607c613af5082233f6f83cfa558078dab4f3f51536d9806af3a041de60bf504af4fdffe341363aabf63c8aed1174ad0390c7a5401680d0af073e078d456788b5c42e160976c7cf02a7879e9777ec2234b560232687a2f296a25cec1371251a10a1d06ae91600e829e65355ad032df9cfc156c73c40449305c53cc8e295e1df76e1048e9715367c5357c27c5e52fb0531b17ff15e52866e34e580f2705321f1fb67db2e0f803f27c405f591dbd430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000787f48115d680a4c932c73175f15fc50fa5e381312c90c530c84da4eb86fa34085ba3924b08ffe30421ea54133c1d829efc5a32ddd8a205cdc0b1c1dd8b59c2c3cceeb0a2a220308c884777a8ef43857ed43730c88a3252cbac15b62233e32755145a55af0a33476984c3465bf8ac30a3ad1296cdcc7dc289ea88f5412abe128b7a51b3e555caf55e0d01f612b6f7019a7db9a76adaa7f5f34eb8c5b1a08d7369339807335c286343574af42527abf7e00a6fc1600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a672814591e830049126b09ff30cc317c1ca11112fd8247c009f83fd6504037ff6db7759d0d1e5c90ae9f21dbc2e5472e23101f78b14641658dc903914d874aeae71967246b6427b453133b9fbb265d8e027a68c4b7c82d0fe905767c212e4666dfee759b12a7317fed8c5ca7492509fc04e24c73a3ff6b64d2ad24468249187a05984668c0662a23b2d66fd237817e71bb8f55039c7d1ec76dd621bb234250d650d74498312002d7d661703a4f397c27d89566000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085211e3c103fcb1926dce62339f5ff69715f03283159797cef6f8b6302512e298b8a2f2fb5b8ac2402e1be6f9f224b00c5ad6e6e3328111b4c31d87c9915613643867c13e3f20076fb1f3841585fc80d3b011e2a770dd94b17b7605a900f55488bbd815ba039545fd098b56e744a2e0f82f9543c5f35c90a9b3a5c0026ad2f17368096147792c2058adacf72205e6739d1baf412f240df5a4dc7284de1ccec20aeb6da52136bde1dc821853498f2bd32e68841160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d668892fca57d92620849f067e9495402bcd2c65f2a7ef529bf3ff739de56538cd16b36b7653a140d8a8b96e8e61245dbc12304220b51804da67ac0e95c1f8569147431cff955743c4350b731607dc05a019412157bc313827372819e2d7d002c93e610778476440a6fe0a6fa2095c1deb920c5957fabe599957443519b15f7afe0dfd1f08e619719fd7e32ba7b3bd2c5d878c158d1f18248cf74c2da336eb0b980673066229be58b4bc2c5326c1042adb5c01680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee19ca352fa2055ab20e5a673ed8670982ffd77c02800275ace0533c8e1e28083572e6318f89db016f1f2b3b467f64317a1fc514e193ef356b57133be0b25544b9fd9c011d608c7e38bce508f42b78008544e040cb32e869ff54201fd143ad3b4a5ae065c090f700187b7b72b56ebd3641c59448d9b1d8643b1fe724147b063ff12fbc5f85940a3b03174128e310465339bd5b0450a4f700da067d008d0b67237176912ffbf20c0de07bc020bc05a7225b48ab5f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf00c4301002151e60768a79c8987402fbeca27d3223d37b65faff62b4054405d3d4c852073b7e33cb21a03c9d2254717a833f5f23be71084f47200f562c286ca20925054817cf37e299f62017814c639281066d4dd7fb4796f20f1953449939ef189c3a01ada007a25f8a00cf46c60064f6d9186ef75767c21d7a54bef1642a6a94191c1083da2560de2b2e9cec7a0ff34ea770fa66f472de2226682cb23a66ebb54445cf33ab7d20f51c41579bbe42ca3f202c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006bf0e43100cbaf5da6cef2357ef2601d9ad2936cb3f4343b71b5ce1fb9fe2f64e83aed606827ef2507d1fb14ce76f811a1d833401561f05cad15a00201d19a71b1ccaf7c2c72d32d28a1f2478b77f14a70a0863e8cc2285720594d2d0e064d05de442d6063e0ea5efdff557a1261731024130a14feac49685608744b7ccff1684f090333799d0864be1b6e6198234c00e8452a769c5ab141b5db8a2af0f0d1045202176ef7764f4290190031bce12b063cb4860300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c2c0b67553be24eef9da869e5b8a81416745d4124642521bac5ee2762dc452575d6b20f3861a118c7d4df474b27605ffd0b025ceda96316aae830467b58ac0c90c22c3e8123f73d4b0f706c5e6a54312d6ae6769b9ac84c168da2628781af706471e41772f9af42f976596544dc5926ec24e61bef463a129b5a8070a708762ea547247d9c422e5f03e3ef3ac0d2935e95997845eed2f1216fd83037273b4536163a33034a44336e5011246dc7eef12f1164636a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef864243a336293787643c232956870bb60ad706e75c242bb2fafc15b0226025c8b4ad7641e6bc2a8d53a213a8ef3c3cfe30b448f29f502955c662002a06a47eec70f7773778c76f1fa6b519a4630b2519a05318b13b6801ed5dc55029e28a014c0190438c24f029d6fd1960daf5ea768a552930a6502b058c92276f1b388a50bd7a8c121968f65d139bea18ce7aaa0101f0af263b5ae15579b83f0bdc2bf750dfda0f67004c5f6a1761a12317e3754a29009d1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028a44005352fe961d0c9f35d89afb9784a30f95bbe9d2b41f7accc115f783018d569ab7a1b039542a29b6a01f4bb9a0d4b8a4e5ce081af4cd360d761de40ae0b92150847d1f5256ed30034429a4d105e7327746963bf223bb68a724732a20f24ec06e75159e3c034e223534800a6015adaa6d25ccb2c565290fd823692a06567e3ffdb15ff372b08925677352831043f1ba083604f2ea83374606d671c47036ec97e5f4b55f46b79b74b4f1d2b117e66606bbf2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035449414d9c6d209ecbc6c6a86542d07ab410f2cd13e106df2b26a792f4e8c0b96266b57f38bcd4577ba172fdfcfac7608336c14ef55b62634bdc84afd26e54abae3251d9193e2799f38b45a98578448618e333423cbc878b1004e49abeab7203357186e0cd7f9245e79f2737e02255170c3d433773dce78d61ce141d1ec12358dc8eb745af23e128f0fc65be6c9703c1a96a80ef3f8551229e8b1628c0fb417e5849e47d30e2f1df5059802a0b96264c5f3ed3b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004646ea7833c9f14f05ae637710e295749b4da70dd63ac214bb8765278cecea46d8c8056d8006041843ecbc26060446038d02a5465a4d6129e09a5c6a54b03e560ad306335e5aad722b059814b662386c686b386357ee585daa136f43228713204146241bf646ff6e7cd7956189b3f51fd0dd183f2f86a13ddca1257c85d1356dfa7799285808695f80f7825aaa345756e7d8e973f67eac11712e8717e956f932f7250b07f7f9383ceb4d210d5cb31370f270006c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000782ad1654fd88212e498a25dbb392817625c5f3c6057323ab58b6b7545c6c64f9c81033135b3bb39f2918905aa7ebd395b8c7c3343eb4613186ffa0b8cdd1f1ffbc8cc151de57726fac82b1b6e3475149e7dbf4f95b90e4bf7b9b00869f73831e79b6339b1dc965c1748d51e5fb461644644eb5fe496f355353fb4530241e62119098f36bae50a3a66a7f13d6b43a275b4780a331efe4210d1fe480989ac2e2e51475b14d9638d328665cc43aae55c49eb12e91e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e4588295fbaef5cf9d18c547953fe7ba05c444fc8d092321b0337020c6608300de56477beef791ef21d2244ea26a229e79546090418ab3233564b79a4f1ff6e0dd7401306a53d1c605af409e5f509153fc23d5e29c1f53876a9de20695f177777564146d1904f4431542d277b96ae28f3ca1f0d6bd12544986ee24fc5367168d5473a25e8553b379e9f0421c8327b222d57a03ba25b7b6e24f2c8681807463153079343b2349a3bf4429657a3f0176939be986400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c1ef22ca1bdcc6b2e70281e264e401bcadb5b03ca3e97446915385773f1a2003c379d28a3cb0e1a6b0fd4710d99e80ccedf1d6ad483d15fdb0ca65da28d166d59e95f0bea8a1a2226aac611bc9a36142f945125ee877a6b96189a69ed1d754a5ec5dc6eb5f16d44ec89d37c01e1e54f11d94b288658a27ef14ad651bd072a18db192e044005976115e8ae721d992a580a4bb01e7237d047103180375ebea0726740fa1f9dd7f66b35fa4816d677e52410f32d3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001967692a6688ec0877c25e5fb91aa65580a3376faf73e42ca441845fa2dbb32b7e31ce137fec44440458c01b1a94fa23cb6c6b545aecbd34ca7a300c7c8c3f3de0a8a703da3c950fc112eb74a96db9303f2b0e09ed0b283a7d669e39eef28556e2f22d259c25ec7c781129346b854d0fd074be2f7f67fd4b5cae8828f349cc5be33f10144d074614d031622b062e8f37fd2f8c0608d4f242fe60622f08f8d249e4c6827df279bf5784466462fcbac77039b91e302c0a7e5df4420778fdb49c131d3f842fb343b3616885de1d33046674d61ae3624aea062cd6620f61074a0b144855ea0541d9523d997b115ff4f0b276205c702e8e572f5819da097410919751e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f4a2db67c911e3a4deb0d1b1f9892d45fc4c65f41d8355c06bc374d3e5b90a47d06ea9e1e443c6a1102d89246b4a6051a4e5cf42d7f538002bfd7f94ec692a430752e1c74eb14b77ad2ba6d6dfcf9687a33acd86c030fe36e8df2b63df4d5011242d83c0b10fef0544560fa094ff6206ae4449d2914402929ac16fc5851f8da706402f44504977d343447fe2570dd64262c79a47295b7615eec7f540d60ac692d70824e7b319ac406177d1e5adf24596a8a60ee30398fae0d11128d22216be40b44c5273f801ee96891c8352b97bcb13990f8d62080500467f347b21cb0b984534ee99d2bec2175350d3d7d583f280021a3b07528492761551e5bc92b4d30d129e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fd4481d3015969d54c1a3f5407c62791845e9ce0c1ebee21744abc0411cb521226b23e221c6af472a2354453f658bb94e96c83a67f1d873149d654e27921b99197f8c9804c771bf2e938f2e5335013f275cb4f57147e7162d9cdf6b0a31243a0ab1400131f911db481ec4f24561c77f66863e0c4ceeffd265807b9516b1e5b017ab506c4eedfcea3aa7782b00fded3416206eb820aa0f10603676257c0c027d073998197c924041388f7bcb3680275c10d0f86a1c72402139aee9f9422cce885144c5273f801ee96891c8352b97bcb13990f8d62080500467f347b21cb0b984534ee99d2bec2175350d3d7d583f280021a3b07528492761551e5bc92b4d30d129e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3ffa35dc49feeebe5e3c370b3a028b890d6146af6bad442d19a7a05376614b0a61a4577d209118343b0732d5762ccf6d2b67bc4e6743d84707b29d0432db8b3c228699192779151800cf06e83be74a652f362cee60b43b393445e40f61b78d4f2764699c17e0e5ae4672bd1b002a78f56ef8e7b864e3617015c58b1421f25b8276040ade38d7e4e54ca486d31a89a53067d4913933e2308915e424a3425506752c9ef26e782cf0cd381f45ba6bdbadcc0aa551d0486ebac02a2232542ce78dc54aa603814ae529fb6f68dcbd012de84205cf1ba70d4bd4fd4f4a58c521e976865676f61f01a3285339eadc61453281e31b7f88e46c9b1e877e4ddb33101054f95c162cf00e9e4bb11e628aee74c8b86756f40d1445923cef6aca48140606c80347bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fc59e4c534f3d63461f7b644fc34c734071516c4105b7431c495bc35e23f51a374a2964317899d237ca750e7a9a8df612b4328c7a2dcf845bd76c6972dcf3d71d7b9e917cd7d1017e40fe9c566d93b04155e96960b22a0e669127ad2db4a5566bc0721107777c164d44ff660445dc47584cd3cf409258372d8a9620093621f96a9368ad3d92210d6461783c6ac63d5e4d9c53896208ad9113f53058601435577c1e9c44745311cb5daf14457698dd2115052d584b6882dd32ddf4a77c08aea3682a32ba4b45cf973db201893798dbf72f8ac7b172bbf31e140226755d94074b4ebaf0d85a3c01111f2e98124583153e417448c912fa6d640a7236c91c0594b8000cabc718392cf3248b4dfd36202d5e724fae3776082c121b00988e00b0b90242d4ab022375c3cc1e33673905b7d73740a1ad9036eacbaf6987a00810689b6139590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fa195b84d2f5c923aefa1474c7637a968dd008e19086bd216f1b7b2117c60931ecd66167ee6f91178cff5ba17dff848435795736a0675ac568995a32868f88738086c803226630821ea426747d57a8011ef36ae46d29ea352a2fbb70c47fb01348dd9b051748003635c1fce6b15ae534d5a015c687bbc731a1d0bff7af096da276f0c1140a67a7a0f2ac47a2fc25bdb1c7053287e500d9750f238741afbc0975d1e9c44745311cb5daf14457698dd2115052d584b6882dd32ddf4a77c08aea3682a32ba4b45cf973db201893798dbf72f8ac7b172bbf31e140226755d94074b4ebaf0d85a3c01111f2e98124583153e417448c912fa6d640a7236c91c0594b8000cabc718392cf3248b4dfd36202d5e724fae3776082c121b00988e00b0b90242d4ab022375c3cc1e33673905b7d73740a1ad9036eacbaf6987a00810689b6139590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fc04e8d772691083ea059f65452eafb361c036650d773f45353d63a2b1037f6637c7e7f12929b0840d9d00648879b5077aeed867a1e7f76401c542d5be5d8ad3092b177327ccfd63333163914b6b7db7a2aa6eb69f2d99c0a754a8171d0ec16075de3b558ff333639fe5d3665b763686096849c39139c81145d2a5e41472fcc53a3a0c91534d9b948fdaee51a23010e3433f868115c19b66999a94858dba629422d81903e2e31b35815d8ab1095a0f04936d4ad40d5f4f16c6e928e17bc3a5d78488ded2ae333c775b71c4717ca89b605e3ad6753541eda55f084700c28a7fc59adefb4285ed4a1719b5b8145451dcd64701b2e238e3b7b63dc125f5a90823168162cf00e9e4bb11e628aee74c8b86756f40d1445923cef6aca48140606c80347bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fc32a1b0694c1705f74aefe4090d5a14b939c2154e58c6051624fe448c7a2726a3547d01096eb1959bd4c204af556165ee3a1d25e7aef011d7af79c59cc8d3a5b41b389524c250868e7c5ee1b6d67d014cf07d43783c0ab486b098b220eeafa6413e7b93888dfc93b34974516e46a2e43d8623d6bb3b272600ce1865ee17e9014b6ef7942b88e5c4e2f422e128b65c849fc9d3159a2250c044954d409a1072a2f70abda68f2ec042fa8ca2264ccf7b70245b8994bca9c0555439fc774452c7347c10efa5c14e3e04f85ad671200ae28284143d5234580153af39b2b61eefabb44c190810de1319103501f137233b9a7087fcd635c1f6d5235e3628910b65aee584a62aa47a57bf47df6b8964aa908457c3288a3758219c74bdff18706fd30555412b45c0297fed31a6cf23c62f8dd43101f016d2ace3f28438b5a137a252e6723ed8b962c0a9ef76f14972e0277e6836df9d906373e35f7324d37c60a5732420413781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a1c92ac7c519121544229a3047d950416494279166ef59d1831eb3468e198286a8df01e265cbe1a40ab46e8699f67db4e65a80659a8ba695868bb8e48c9cb8440cb36c1085609d82eaa1b8850e0f895406de71d7e2e4c4b0834bac54bfc081439d813d857810e591b22cad22a777b7d26b22a954b7e0ca83dd5b9921276c332544d27315deb48b007f2f6a109cee1765ea27df430bf8c0e47edd2d02b2a9a664370abda68f2ec042fa8ca2264ccf7b70245b8994bca9c0555439fc774452c7347c10efa5c14e3e04f85ad671200ae28284143d5234580153af39b2b61eefabb44c190810de1319103501f137233b9a7087fcd635c1f6d5235e3628910b65aee584a62aa47a57bf47df6b8964aa908457c3288a3758219c74bdff18706fd30555412b45c0297fed31a6cf23c62f8dd43101f016d2ace3f28438b5a137a252e6723ed8b962c0a9ef76f14972e0277e6836df9d906373e35f7324d37c60a5732420413781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a686ad4756822a000262f8f49136fbc3cffc0f564724dc44e871fa64bbd15c83c751e9d54e1380357377a7f64b6d7fd17c1e684428e571b6b8ffab779b5c4816ba0f2652b6fba7f6a56d07d11c2ae2209442ca70df2919a08021717121f26f917c97f497009b44d7dbdb7c1239bab0b138ba06365d0d8d9459472b27d155fe22c77be6748d9691966f3636c578ba3086890702972535fba42abdf7274c34bde72bee1a516690b9b22026373207272d8447ae3981c193bea77c2aedb18fd30023f4382ed2d31839959e75e3e130bcd36115cf0652a1b55f15ddbbcc167fd8d7747baf0d85a3c01111f2e98124583153e417448c912fa6d640a7236c91c0594b8000cabc718392cf3248b4dfd36202d5e724fae3776082c121b00988e00b0b90242d4ab022375c3cc1e33673905b7d73740a1ad9036eacbaf6987a00810689b6139590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fa1a7023492cca429d35cd03280043c58b080c77c5cf8380501a3f44f4abaf229f2894650993c4d4fcf83bc5db1b4055f1cab6d2466200a68bf2ed362fa63c2524eb8874321727f039e9f0e02e66e485470e0661cfe41135b3749a074a2db8473e074885cda3a5b445775520e4ee1965523ed784958b577489468d20b287f1c3e129d753a1842b30fd4b8bc38087e0471f3ba9908804dbc009bc5b759ed469472ac62831c12e8976cfb707d3432c6af525762904d816bd135fc0ffa4229e31326e2ce5e52a19bbc4ad9d23932a154e214227a0e531cb3a24c51a9455e5c66a4751c48642b1735f10fe97bc744fde0a725740ab7000f4ef8578d49a20cbf974d5a9bbc27351c85cc44e1bf505d4922293a9842f274f161cc47798f5e1c192be67755cf246f8435dc55068b0b6d9b8f595307ea7301c0744712034bdf39c7dd6d20f883cd3df955ae3c54a3ee243947bf4093b0c127b8dbba63490617568bb8d15a501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fb1de8a2118d3ec795d5f2e4149440a5550eb15292b268871adc3565ea189fa56aea5d00996518673d2d53f5c666f9b7204bdc21edee32a5a9857b069138dc74a5ff0da3b3e4e8831b96f3b5f8e629426f064cd105affd91ded71a53a64da952c5e9c4b04da69da7d6501197cf3d60a28aa613522a85fde62c952305503d33125647def40ce6a9b72f8537625a6da8a2b8a68c70086d78c33b33cd226b3d001027249a0773975777c68bd1f5bc93fef53ef07101da930a32fcc7168592d24a823e3c18c1527bc8a31786bf034f130f865195885271fdac75efb70b60f99755c258887260f05254b621e43456edf1797787438f404b220e42663b75622bc9ee12fa475c3718a0a587baa349d6f8c1df4448f15425786150376fb188002a5929a3c0354051c9e7cf427698481596aae1c1bbb36074be94c6a78cfdac250992d7452da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a79741a2b64d22c67b6252c7d87ea33690750026c8520aa4d9d4b8316f44e077626ae71035b1fa23c6f0b2142ad8e2b6bfdd8e369eebf7e7e966a072476fac50a66e1e964527ada22250fb5646f3dc0391780ae6b9c380d116d28b97947a6a244368b090ab41afa5ee9653177a7b55d7e1755db68c496bb23a0d8ba79e728705a07faf4703009ad2affc948072c20bb79534ebd4df588db5dfa6fe50de08ff904a4792425e6bc0063b363c80d9a281a39d480507da66e65150f88c81ec7771531312db76c2fc0271ca240bf6c5ff80e69c98f160a6c0e5653ce4bf07315b6f0324ee99d2bec2175350d3d7d583f280021a3b07528492761551e5bc92b4d30d129e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f74ab9632a0c1ae0b9155fc6214341d0c1ba496077976780e946f694255e2660315c7bb3bd3cbd34242a9fe32df4af10308d8ba1e20f2075d48f71f49cf0e8a12676fd91d00d6de013d9b9478ffa95b0e325fce09db450079b47a3b29de9c8e29b650ba0ec4a4967010c29d2f4989451eab411152ed5a0c17e921196a94618e4eea89292e96acf2431cb5485853caac764ef83e3567e2187c6581a3191bbe3f09239eed3b79598946eb182a4a630e1e6b8d7e600d8b66ea6fcb7b0770f27fe914e2ce5e52a19bbc4ad9d23932a154e214227a0e531cb3a24c51a9455e5c66a4751c48642b1735f10fe97bc744fde0a725740ab7000f4ef8578d49a20cbf974d5a9bbc27351c85cc44e1bf505d4922293a9842f274f161cc47798f5e1c192be67755cf246f8435dc55068b0b6d9b8f595307ea7301c0744712034bdf39c7dd6d20f883cd3df955ae3c54a3ee243947bf4093b0c127b8dbba63490617568bb8d15a501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f66b64e65d8e87a58fb6dd206938b0d61e36e4130d2e63b0f9d95684817e00e5b92b03e1d949cbd1a15687c5f82c4b42172b9dd7105cbb80495cea80f1ef5b00c619ba51f30de6d6baf15233dc06e9353d425eb13735e8d62c6fd62587a7e064813d0972d13804f5a5286555ed2cb3f30c4ba736a4cf8d548bf91dd62107ed8222d93ff31e133206c9ff93447943d2e361db70f08e3280c486b0d1c3c192cbf1adda359393ae375031882d25df25f6a606204ce3788baad0a544ce4077534a36475fec87c88d964254e7f7c3af344f9220c9b39434f2a5a4ca8105506ab6fbb46b8fa500309509d51e36cbc6bb738b023e1cf124fc13770625348ac068f1cfe20d6b37027c40e5b1d0d1add58f0c7f51487e36f7130615401a2a2aa0adc8bac3bc829a8077de6722b63dbf717a7f2d45045caa51fe5dc5c20f2fedc40092a0043ed8b962c0a9ef76f14972e0277e6836df9d906373e35f7324d37c60a5732420413781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a2afbb55b737b336428ad764c09068f26ba42630015a98e0580ab2378719f3802a2a9b2598ef24823468645184d80c522f8972c31bb568e563f22806fd50076269a9dac246d306a5c7f666371808cf61975c9647d1684ca609827c5125a5c7b282ab7514b24da703d54b4c047d729fe3ecceaa5713f22172dd4e865258be7e126859392523f1ed66f699bd60e1bbaea45929b1608fdfd473aa31f1d6e7edaed4e3998197c924041388f7bcb3680275c10d0f86a1c72402139aee9f9422cce885144c5273f801ee96891c8352b97bcb13990f8d62080500467f347b21cb0b984534ee99d2bec2175350d3d7d583f280021a3b07528492761551e5bc92b4d30d129e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fcb5e35414f3b56065b2a2b5112ae1d3c91001072bc7fc21bc8784071928f113224988a203f687103233cfb452d2dfb278f5c831d68ec7526a0a5067d75cca0646edc3709af3a670e63776055409a7906e2c63a21aabb171101500b06769e3855ff19e9095a2fb516aea2f1466b1f9f13fff0ff2651d0632ed3f00720a0cc1a2b5b728b62dfd08625473fa67018b7f475b6e20d1699fd1d5ec300900b3fbf613e56ce035c4645a52ace2eff0f102df82023aa611f5113804d4d967f6163ade2549431b03c36d12169c3f5836ef406ec6d77c8b11a538eb30a9d3e0e6945cb19273b718b6d7ec46f3a38c945484ea90d05b4397d4eec09222589707c1a6e09f8351b18a93707f8d410258d5150d04fd71e06677f7a11027620ab1e817201e5ac6ac829a8077de6722b63dbf717a7f2d45045caa51fe5dc5c20f2fedc40092a0043ed8b962c0a9ef76f14972e0277e6836df9d906373e35f7324d37c60a5732420413781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a41f2b64a906edf15c619773fd599b206593a4d33acdc9c24d01c8f6aaaa7521064a7fc11d8385c5c68809f0e0ac7823868f8c022f369736ce677397a7bf6ce7c2ebea116b31f0b7b83a152063d36e43941942e5f88bfb34f8bcff03561f4db39b780e2000ae20e642c721a7438a262014c2c327876ed371da4d3c74eb02e1f2d08233e0ad4b4290fb700730e01625d1186dd1c4c76df507c91e0d825b4c8ef4e27a5562d8a68e53338e1101922c09b11e003713dd82a3965db8bef1b7f82a80e18dd622444e3dd4d1cdc7c3d1f39d2681ae1885f03fd3a657e36750777522c47adefb4285ed4a1719b5b8145451dcd64701b2e238e3b7b63dc125f5a90823168162cf00e9e4bb11e628aee74c8b86756f40d1445923cef6aca48140606c80347bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3ffef7a4068d6d9819d7ba0a03f863fd106b78371018963007fdb9216cde6fa3759eb9ff021993ce1cf1d84e0000641a0c1dea1278ef02b355caeb0421383bcf705e65fe5de67ebb5c51b08845fb04a17b699a313b5fbe5c33bd05533f6cbcb91c78775e643e9e054a5715eb6ffef1922e9aa8942dc47bf122a0ed320041cf0b571723a61315338024d63b720acd05fd42407a986746c53b63834b8159b49571040b2fe3670aa64a4e4e1f6943acf4f2757bd9db091d2c087da90abe591a361236312db76c2fc0271ca240bf6c5ff80e69c98f160a6c0e5653ce4bf07315b6f0324ee99d2bec2175350d3d7d583f280021a3b07528492761551e5bc92b4d30d129e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f1807c1187d0a0d36142ad9794e6849650a0afe01faf50d1b187d925a0dc4e47e14746f3d61b9395aab75ce019b3aeb0444629d7308134d3ccc3ccf3d6906182ca0f2652b6fba7f6a56d07d11c2ae2209442ca70df2919a08021717121f26f917c97f497009b44d7dbdb7c1239bab0b138ba06365d0d8d9459472b27d155fe22c77be6748d9691966f3636c578ba3086890702972535fba42abdf7274c34bde72bee1a516690b9b22026373207272d8447ae3981c193bea77c2aedb18fd30023f4382ed2d31839959e75e3e130bcd36115cf0652a1b55f15ddbbcc167fd8d7747baf0d85a3c01111f2e98124583153e417448c912fa6d640a7236c91c0594b8000cabc718392cf3248b4dfd36202d5e724fae3776082c121b00988e00b0b90242d4ab022375c3cc1e33673905b7d73740a1ad9036eacbaf6987a00810689b6139590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f1202f363862bbe7d71d2d731a0aca31b9bdcdd4b81d8360bca3852511e5fb67c5f1579787eae58200382d44d9a4b9a3cc3f6576d86a7ee66962aee4fb391df4d906e4119747a88005a30c93f8ea789110e2e63773b35a45bec773720ae9aac5e6a08c4296fcba0103a54bc7b85ac29299909cd42ccecc96bcad99513a7ec4f6b934f1969bbbf5f796b98cb059bc990074e1a71108d6eda6b2b2cd960f02f7945dd9af253e4d4c45ff6f12200937c27581692d501dc82233157f02e3d72326277488ded2ae333c775b71c4717ca89b605e3ad6753541eda55f084700c28a7fc59adefb4285ed4a1719b5b8145451dcd64701b2e238e3b7b63dc125f5a90823168162cf00e9e4bb11e628aee74c8b86756f40d1445923cef6aca48140606c80347bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3ff53a0402fce3306c3fa4b71e61b61440887e2b05c8ec2330b79d5e60a8a15267edc2d66367921012ddb2ca41771a5855088d7c0216f1cd3bf3ea7d66021dbd3533e3b36fed46294680566c21ab221a67827b9878b58ea23c3e61d7492e7cfe78d485e876756a7a1e7936711e8ec445432400e00455c4fe5a3ffdf84e2254892858b6635173107f1e21be3106b42dd3493e9f8641ca3d225eaa476f101e5b4f2f906c370f192f8163d353950116b8a643e845813999789f46da05860e66162a5e2fd07963363a766deffbd30ccf57f12c1174fd3f947f186a6c356058467f2a582063394a4fe5422cb38a734c0fbe840d4eefc077c647552117bd201126c97c0c4a62aa47a57bf47df6b8964aa908457c3288a3758219c74bdff18706fd30555412b45c0297fed31a6cf23c62f8dd43101f016d2ace3f28438b5a137a252e6723ed8b962c0a9ef76f14972e0277e6836df9d906373e35f7324d37c60a5732420413781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5aa9a6726bddb56865c701fb038d249165fc5e777851ee0212e096834301179923cd2ee6354dd22d70c960133733d5456c2455e6706752564cf07a54778bdddf47c64ceb373ff8a83c00b8004ce20d833b1f2e01003f613d4aa65aa8469f4eed10a952d55359bd667a7f46150325da424d4d2a333f826bc157aba9fe277aa687102f7edf7019b895072443345cad907439667e3a2b33101f6516997e17e89efd72ec81dc1845af204ee2311d152321910558854175d2cc1e625dd1520df3423412560ee241f6832e58eb56883d6f3c7411e84a82715db64d6d928fc72430049c66c34ba93e9b8aed43c26f3f2546446c2098909f3fd0bccc4432ea26498e55681b41f4752b1895391c5566ea2c7b761c74eecb9c0fab83374166227505bebb5b190354051c9e7cf427698481596aae1c1bbb36074be94c6a78cfdac250992d7452da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a165fbc2b20427111bb913e0cc114a3249005d96b16c28b4a714e265cbb184501643d8914c045ae6564a5d90f38cc897d9df9302c219c922cf9727756b10356523d10f8500954ae2d5bfaca6f27bb7c1c23375b1c40776179bdff7a794d70bf17a09be47036cca80f4faddc21bbcf4946df8924073fb201618751bb7cfa3bca11460cab547bcd9d162291533eca72651285d2d17bd54a60605273875773e4451b973333347a8b28270efa38377e89d44c022c8b04fecef454c2326564390ed5302a32ba4b45cf973db201893798dbf72f8ac7b172bbf31e140226755d94074b4ebaf0d85a3c01111f2e98124583153e417448c912fa6d640a7236c91c0594b8000cabc718392cf3248b4dfd36202d5e724fae3776082c121b00988e00b0b90242d4ab022375c3cc1e33673905b7d73740a1ad9036eacbaf6987a00810689b6139590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f52c293375460c62b41dbbb25d1795a6e95b9b76d3cadf95e416c4a29e3c0512fdef21c58a769c758fece0100a54d411370501822dac39655550d463c861c6951ed4685676c92ce7a56a31c4ac12b613bc0f719134a2adb72052b1539bbef645b7a9947263d6a5f371b832e447271847126b25a2048b8c3252f675b1b9ed22d2d010b493b27a255124eeda141231ff47a6587625c24cb9e6c1ba4a0033557a57372e6305153843d4001cee002ad03f556c6fb4e58408066521534440de01ce4119ceaf165d2c9605db14d137328400e65aa3ddd69b8e6704bab93197cc7a9526cb61ef10009389f0a21e27253a11e500a53635e174841967b61bdde1de56de14e1ff1056541bc954507cc262ee9236c137c3b524a7cb07a0a96ee623737ae5c7148e8c0167523c82c88ca920295f5a624319e0d04599a475e7e9b344aca9a0051da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a1c0c30257dda0329b8c90226d4b0d503486b6f23f1d8c667c4584e5d2133264fb9ce5a28b582c41129b4ed612dc0de6feeacfd00a613780c43bceb2ba4444018fc82863d5507874fdb8a0d6f6e82ad495420462e425e885b8bf2ad36af529507709f8f0ca7aeee4a748eea5a83d7947959ecba55bf249a07ff8e7231d3e4331864b2f07cd85c8b4390717465d133411300332446dd009274afa99c75c552b87274f40867080e445e3eb95154e312d95fbb2f2c70043beb0d5b7a2c1b39a39d7efca65e6283b3752c38c84625654491526d2139245ee4526782cb592a531ee943c34ba93e9b8aed43c26f3f2546446c2098909f3fd0bccc4432ea26498e55681b41f4752b1895391c5566ea2c7b761c74eecb9c0fab83374166227505bebb5b190354051c9e7cf427698481596aae1c1bbb36074be94c6a78cfdac250992d7452da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a3f64da6e994c8f1a8113074a31cd581d355d7443c794424d1609c534bc72fb44fbf68740c193103cf4ffe343ed23ce5f4720fa609aedaf3378c5683868f7f43e8a2350577a120b71f93d6b49f667ef3d99ae4f52ace0c849699107157ddf2b5b39ae7c4895240862886ab52e1b8cea403bbe726b770756058e4046181effd153178bc93dcc11b37cb86f401a28e9f11073f7761fa3d889271b62fe2172002d1dd504c80066f5a314fa51594ba31b76561388f65683ee9c1a6d84e3470e48ed1f19d9585f2e7366263e2fbb6ea893fa6e8dd9c639d8993b694f83b7519e3cf16f85f15c0204547a77d002a91b0be58c3894889a51c9380e310793a968273abe30589ad634225a360da1c56369c08e6d13b3fafc040480d560eec4000c33cc781b12b45c0297fed31a6cf23c62f8dd43101f016d2ace3f28438b5a137a252e6723ed8b962c0a9ef76f14972e0277e6836df9d906373e35f7324d37c60a5732420413781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a889f9415b215742c13de683f768ed42359bb52721e7ef53da05db87dd4d6f405684ab03782dfa71e3021732e1cbb0d4a16249b11240c613d45dddd34b99a6428f881be2fd3383b21d1680603d64b94653e18514db2a08f5e70abe90735874b78c055b4175cdb49181b47017be737c1776ecb774f4a046b0c41f5d93711bd7f6c64b2f07cd85c8b4390717465d133411300332446dd009274afa99c75c552b87274f40867080e445e3eb95154e312d95fbb2f2c70043beb0d5b7a2c1b39a39d7efca65e6283b3752c38c84625654491526d2139245ee4526782cb592a531ee943c34ba93e9b8aed43c26f3f2546446c2098909f3fd0bccc4432ea26498e55681b41f4752b1895391c5566ea2c7b761c74eecb9c0fab83374166227505bebb5b190354051c9e7cf427698481596aae1c1bbb36074be94c6a78cfdac250992d7452da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5aaebcc46ce54f1a077b595f742ed4c7713c71043ad91ff536b8528e10b8a0d67d8068393a0e66825e0c65e312a261f81f48b5d05ea264775c9bce657022fe0c69ee1a9756c49b7c388e9800462cb9791da9603e1b908c9a10440378052441cb21710aca276b3aa856740c361714c4e07b0420806f1e0c636935f92669f1f24878513884430342d26f46482051be318b36659b8d0e8edb055d5cebe7765ce0d3261ba50841fcbc601c517308508d4c734ee185c65fec64cd46ac711a2c473c0319aeb99226d263f54312d1fd7145d468420e633a22e821fc3f4ab86900a7a6050b9d93aa07544dc53f5ab77f5d3310d65993bf53494dd5412419ecef500950895ba475c3718a0a587baa349d6f8c1df4448f15425786150376fb188002a5929a3c0354051c9e7cf427698481596aae1c1bbb36074be94c6a78cfdac250992d7452da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a06e3434d3f8ce1199d038000b6bbdd48654c4d40a637bd2dbebd2761c296cf2808a3ba5b4162451919981a376945ec15b9a5023338742c1b4974cf29560f691bdae7fc2f22fb7c7ba0710a12f6ec24378a9863360023085a47a06940de2b93127257b83ab368960342505d2685759805c6d5534b41f81264249fdc5d0aa4903eb70c98155a3e1f428aaeef05a80b563160473e183e09826f0a603506053fa61d5146206fbe52c52a30fb580fa088044350ba223cb511653df5760a49f977c10084984f3e87863414aecb966903c7ad2b365004241e5ffd3d39af2b08c042b2654855ea0541d9523d997b115ff4f0b276205c702e8e572f5819da097410919751e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3ffb2e516a5640f74d648a390195f29625d7d4d947f36fdf5d14e1012bc476121c723421233821d95dedafb12f1c026d621d34b734f26dac0d2ac0c464e806950e5854c5746a9f17080d21f856f9dcfa40a489543a57328b566779540114701070708dfa4fe6941549ac44073b20671e79e3ae340d0448f515f078734770a0811d0bd57c042b566a63394a41316b8a3b347cf4745142beda604c00f520c49b591b565352064969e31dfea43f0c0eadf96be96ecd3863d6d62cfa342b7e7bee567cb16ea97629b73c0b6f75901228245006a5030d4525fb28121fd39075fceca43b36d7d63091bf6461d042b826ec94633d93e9b737969fac2fe7d1a90d5f8e37551ff1056541bc954507cc262ee9236c137c3b524a7cb07a0a96ee623737ae5c7148e8c0167523c82c88ca920295f5a624319e0d04599a475e7e9b344aca9a0051da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a43c7a74218cbcc6113cc79274d19725a47a1c46bc8cedd7d6c2eb173ad77c31a9962a5272ae53f47af76a2199f05e05af719bd0d8b7ab07d2337d2359b440c7372f15b5b57e1ce1e5c80cf6abf611131c8421618b5df47715142ca1fae930d5b120449228f754b3a7be33a021e614a04393e0c49165d587bca995273023f834af417472812e84f31c9af1251a3a04b2632e13e5710cb460e32248d05dfa9ac0bf661551a640907418a70da5bda50722c67e78f20fac86664bda6903e665882255ada22225f1e2b3063589d7150b79f59949281624fe0e94fa908a1251614df7d96ddc0389b0a86300b5268612af36936295d7b7eb71624197347dd0a0cd2764551a7bd5b26dc79796d768d2650429d3ef42300101f7a7415a1c9592eed22f82655cf246f8435dc55068b0b6d9b8f595307ea7301c0744712034bdf39c7dd6d20f883cd3df955ae3c54a3ee243947bf4093b0c127b8dbba63490617568bb8d15a501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f2ce42f193776cd0ccbdc7e697a9198640fc3f25dded5472f15a75a22aa765e6386ba7d5bc9a03814394c0873b1c1b57bee5efc494c19db547b0ecc0f0ffb660ef9092e751d55132e541c871ca101da6c27b80341b6eac513e5180c565766bc7c8508c30264d77731df3393231925454e901dbe035bae990639161533d95864441e6d0007cfad11321b1baa1dc78bbf3e056c066f3973bb3350dd084011f58c420a772724ee05a35d3a3953561b824a485173c93f5df07a6d245f5954aa46fc796090a744b37d6c4bbafe60404a31f7482410dd1dcb6f0671592287441e62651bc1d4087a7befba3ce30c56531259af02eae2802d5f68b9053ff9740f4f25e076772e5a3a5f653e3ab619f3294c14105f30782b42c99bbd5d230beb0a6de77b5ec3943354856624636635ed5ef3817e6377d787080b19bc4af0e16462bf85a723f883cd3df955ae3c54a3ee243947bf4093b0c127b8dbba63490617568bb8d15a501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f910e871bffa0c43557ad502a579d470de5ebda730fae7f698b82636f860c626b0eb76718b86904107e0b765cecd87e3114a18f03b7c56a3f0dd5d11dd8d14760edac6f3f6c783d5402c9f452fbecd95d92798365ae518347f9312517128f9803eb713a7cb12fef24c1f7cc78951a3836aad7c37b1e9f3646bce7846a20621f257b6600727f7f4f38499a6c0ee0b69c48619e222d4645056f3e33494ce1d0f33e6b5f475b48901354f14065623c9c9c6c8280fd740ad32e1f91d5fe61b03474352d516c4fc8789814be4d35164545665c325f6256d6badf3272dcc70a96f8ee3ae449634366428a272b1b050a8fb18c3e08be8d21c44bb732292c97406b56873e589ad634225a360da1c56369c08e6d13b3fafc040480d560eec4000c33cc781b12b45c0297fed31a6cf23c62f8dd43101f016d2ace3f28438b5a137a252e6723ed8b962c0a9ef76f14972e0277e6836df9d906373e35f7324d37c60a5732420413781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5ac802681c5730277321423d4925027d5439a2d045db70ad657fd82052348f6d372d47e76ae929fb6582986733d4150d65d22ad63aab2ad00a5b530903981fd84a0ba145171ccc3c72df7a2c4397a3ce05a62b46046e2ec62e84c8a73e387f4d6cc1f81d563e3f4c0d7e39d20d2e4199721e3dfe6791af45297e947a592be09b34774cc92c9d789f21870862400ee2dd7efe537d178f4dfb1a2310cb4238755d5949efa022660e7b37609d106157a5a4429b4f114e2807be187e61126a2eaf0615f356cf553a5ba41cda80f76bb872ab1ee640952138660466e41b032556a1e479c6be016daa434b407c9a613df73b606800c9993f14ad74622a0bcc5de3dcdb3a41f4752b1895391c5566ea2c7b761c74eecb9c0fab83374166227505bebb5b190354051c9e7cf427698481596aae1c1bbb36074be94c6a78cfdac250992d7452da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5ab5c6234c0000900cbacb6c33f06fbb3f08740412e583824ca12524614394797e1892fc674a0df37232034f2bd91a57705cc3803bab1d1e29d27a9633fbac6946ed4685676c92ce7a56a31c4ac12b613bc0f719134a2adb72052b1539bbef645b7a9947263d6a5f371b832e447271847126b25a2048b8c3252f675b1b9ed22d2d010b493b27a255124eeda141231ff47a6587625c24cb9e6c1ba4a0033557a57372e6305153843d4001cee002ad03f556c6fb4e58408066521534440de01ce4119ceaf165d2c9605db14d137328400e65aa3ddd69b8e6704bab93197cc7a9526cb61ef10009389f0a21e27253a11e500a53635e174841967b61bdde1de56de14e1ff1056541bc954507cc262ee9236c137c3b524a7cb07a0a96ee623737ae5c7148e8c0167523c82c88ca920295f5a624319e0d04599a475e7e9b344aca9a0051da582309b9ab872653e62c213944ca46b7732a7927e731126a9c0d6c2d53b16213781d2a93268b47c951b443935eb6385441f77bfdc27c09955af61459153d5a227b4c5c4ce94766f0dfab535ad7834b27aacc26eab5191c587baa4416db6e0fecd78921c513b867b163255ec6a342714141a45b2320d41c739b1d3a2778485a2d54785bbb34280f2c0e0c01ebc7462df7b4d33e6a6e4f0fff310a22382ec879bb538d4d75b2414f9035e55ee4f6d959a1c1c122777017288615df1320e74a62e50f270abcc7fa7e7d446b090403723b5e67924184429a0397276522cf5c9d130b2fe3670aa64a4e4e1f6943acf4f2757bd9db091d2c087da90abe591a361236312db76c2fc0271ca240bf6c5ff80e69c98f160a6c0e5653ce4bf07315b6f0324ee99d2bec2175350d3d7d583f280021a3b07528492761551e5bc92b4d30d129e2957543ca2f473ca9929a108ed7881cbb55d73173819f18f58c7e217bab7a43bfe1b70950a5bb3dde54501976d14a796221a32ba4790b13a3808c1467a2e476590a6955e1af13585f77df39f4ad0a0b1a699e5a5daeff38b64d9a2b07d67d5c501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3fbb18bd574005d653b647be441e0a0a079ab594482f05e150512f4a34592a62582f299e182a73664d61ec6771d1bb620c4b81765ee21b7d1c1107262dadb6c4144c44056c37d5f7751ce9bf7a7500a44020b9495d1fe97d3e0fe6ea15d66c4303b3916b72e4062d124aad942c31b3e65d7e915b496b306b6e92b29e64f707e67a0194fa7407a1492d47097c65380c7d50bfe86f0a97c6ea416de70b0ed27d4d0a3f92a43910c4347754c9553ea4aea86c11a4d220eb8af753ba57d1479ff24422145b7a779e3f2750e8a9743554070c36e4f45f45448f0a647ff2206e83ef3610a6ce581eb3ec4b798df3d81d9cbc634a4af98653f40d1f3bf0533c100ab9b23651a7bd5b26dc79796d768d2650429d3ef42300101f7a7415a1c9592eed22f82655cf246f8435dc55068b0b6d9b8f595307ea7301c0744712034bdf39c7dd6d20f883cd3df955ae3c54a3ee243947bf4093b0c127b8dbba63490617568bb8d15a501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f434673613bfa47512b76a8061e38d82c7e3a57568a67231578821e523b831801cc2dcf5d5f444c727753b973ffa0b5535ffb59337a831036fce9987424acc82b0917c21a2af42642a14c3d0f7b642c60fd3ac5669f8b80678b0f6676141c2f6546793e245a4e0a19d23a275f90387426b4d2297114353f5a0b3b084143cf303b19b4df29a0aa8c009e2640545379180467d43d3f494a466bafc7cc6f671f444a9313771d8bc43e1de76743094eb1fe0047703b5d25b60d463518dd583478ac3289eef55058d95927e8db1e24d9dc6d30c312181f19dca8309fbd866735cddc76c1d4087a7befba3ce30c56531259af02eae2802d5f68b9053ff9740f4f25e076772e5a3a5f653e3ab619f3294c14105f30782b42c99bbd5d230beb0a6de77b5ec3943354856624636635ed5ef3817e6377d787080b19bc4af0e16462bf85a723f883cd3df955ae3c54a3ee243947bf4093b0c127b8dbba63490617568bb8d15a501aee01b154d817e8c29558c2a083092f5ada52dd01b4455297bb02be2b0a3f79028517167ce957402ae87310be8e0e5098ed3db9afc859d4dec241a195284f576ceb54a1a381370828f15179c567794b849e65e81cef4e3ff85577b2ad9a72ac0be624c23487048ebdf81a2e06f9443f1e8e4712834d37992eac12e5907265034e0b515885460e77680d2c38b3786c15f02d2928a08006b288944abc69a56f924e616adc2c3007278ccf569279f01de9d92d1fc4d94f6779678e3aa85df87967389d3bc639ab142cd116283dc5e31b29ba81385f307206a801ad01bfb50733f5c4560835420e6e5336a6261ebd9a275379fd09a8700644a1fa8a5515b39f75bb6d66214375b512eb54163331e4c66a004c9c345c540d76da7c975f0eacc669fced4b4bec19b45e45644d6322954d769fecb60ba791811ce34bb135419d873ec5ee867bfcb9d828f026bf276d3f572aeb245e3f2ce8386bff65360e97f76662cd917a3ca18bf3273c80366d88eebb019931e00c146558286d7f9e6bb19d3655cf413c56bc330c0980840d005be2b901258d2b0fdef2c76db6b11428b6f15464d78cec248c62ba73d8b57100f6ef021744981c5c7563ad28a7048e6bbd839b4865835b172ef2921056afe73a3e409f18de2e561c86ed48587e891e220e739920c1fa9c55af827a504c1752585d380426aed5f4259de5b96979b89420fb0c07611ec032521054b078351fe143b71932299fb54c0b828ced202c45c831e04d63504b4e067c66110f65adf3487234ea376d9b1daf1b33c7fc46124b65103018f721ec0bf864cf87ac0a92459012eda2be4f935324284194ad59f846220658cc6a1ef279e058744b2f237e3aa855d9c2684def306b5ec2c1fe4fe03fd45ad2a7977593356c0c868a6d037225d52f6810e93de902530673008d5621411514ff4d06016518b156d8a6877225aef7558d8ea95c32e40366ed0ceb1f5fb1377abdba8014eff902344b593d32b8f3f974cbd2334e54b71233a1a4db425312890002f5654e856df0772db2922ddb46cc388e9c214f6685e13cf7835a1273ed055752014d7ec9c97913734fdf19a52967526ba4545ce9376713b26d18482cc840250306135fc71c245465fb7e55eea00819850b3c0942bef406f7006c186e520e5efcd74d6a6964d6660f8620041708890c6a0f3f62a3035575b7efb30b7a59ff59e36ee909ad967619ef0d93519faecd6e8f1c9163dc88442a37440f5395f82756a988003efb21ae70d3739d43f74db6007d65340714efff5bfbe47811d0caae646afab94a773bce4a86e283290311ee3a4d4e7c2bfcd42b45617e92648e68736144bd1e3ae32707639d17ef3a4b750d508278d335dfc250562dde8d14bb06d85c0fe80d740c403c102fbc507ae76215122abe29159f87ac3a6fa3d3663d8a9a238cd6d445127f640a36174853155693461e3d8b3a60adb50d93c2d205fd488e67b42eb77b7875fa563e5d353603f2d764a102af7d3457a64a5b98643bd9eb4a4636d18f4c657b636fa181f23a1151623379ff0d164eef086fe36c1653597a1768eb0cd6639795341c791aeb6e7c5c6d5a2abf9b20c3d1192610113d1a27e5257e0db3d65822beaf614b1ae36d0707267d6bd47c443e1bcc15d2aa5d6e2f8d387582f37a0ca01dd37abf4c2f3763ec9d5f3f3b3863d6996f4a4c47920197c1344dc900af013c810055c277b807e15075797af36d4f70f88525b2a8666e1509a8572903165d5e249312da66714c659e3d05d187fb1cb9f3e3640f242379f2795a46f584c165fc2914360a232524ed3a974b9ee87a5b15fb9f726eeeb67ded791c3f1311fe6a1b6fd727b3382e26ec09c40067c2203dec843c042bb0601640615849b244744398efda23156c5b09d0243473c704824681938802cfeff46ea263b9524c5dd96c885e710019ba9477efc0892bda83f33a48f52225e8ede33379d5733b57edd032a11a96760353040239dc8f644b888929f5ff832554b0fa22c237f358edd43d118ca60d36cca0824111a080428e8e0549f138906586620b2a37a89f4bd8217070b90b9a7ae99fb32e4a3c490eb0071d678dcfa8233fc33a7b1ae30a037c36a2547931fc18ca749c5416a9a749e5568d4edcbf8178f702be40afc64274f6cf5f06ff0f40060aa1c70344baa754ef45542a9d451c5ba81c4e79de863705cd69fd0c4321df3a829777133b9d864bd203b12c9030132603d3fe67f7acd15ddcf0a02f810a743bf01f4517e9727a506a44f20c513ea256638ba53c4ed41c1651c6867e5f0ba77a147bcc519af2674f41289401dac9d1358143710f5d7b4e31a4cf286f3bbe34722685903e04544d416f565323e20a0f4520251e145efe951861421d23dab451019e304c6e28e1e47621772673946f0d034373260a56730d562c6267357d73e139e945b611ac3fae0caa0c3b047ac9ef19e2dbb730f979a60449d590080dff85471f58d82ec22e0f7b655fca0b64511146d824cc3ef1646357e3fd8369762aa952932c193fee5bc63b9ac7d001629c6b302422b96fc9140b0d7864cb52a09ab04c5ccc0d23fb00082c7996262968dfeb3283dac16b1f7aff0207898d069521a72a67b01e1a4b309b519458261c701b24299774740e2cec8c43c6be6f2f6e8d9d344fd5f84fc7d84b64f478c44058efc937f25ce1095de8392ab11b0e5ee8b45f717a911f6b42cdd00bca3a2a51668fd72473074a2e0d9f9d1f80f20e75f9d99927d68ca33636ecb4571bd0644c0d3f3d2f0dfc75007cc7c26f56e2722ecc29e965b29f1a3b7322675288f45e3e1d305505389917575b11d3351413c342377984314317c111df32a5002ceb4b7670e6fd47af58cb6e1b8cd30cf42a0a660b5401571a5b3f2ad74b1f798937ee7ea1aedc19f6edf6697472a542174f9f3bfc3dae2d7054c0399849515bcfabb063ed70905cc9e3497db6ed73628039ec71b1a62d2b68106c55022eb5691bc58960595e5a70540d5d19fd72981c2c615108c9ea8c2db63d14421d42223cfc6f1f4559ee2c16e18b2f1971b5f55d9080497ce77e3a6b18877039a0798c34b3da74306fbc8932cb3a95197e78a834ddad1c3bf820a05402188a33c422224052c7f837f8590a31259954407c6467159224db794f13eb3c8fc82d16499ef972c62f88008ceef34ea2c3866c003b7e2e1ee8bb3e8db02347b0484361b7065a48eb6d3b6c3b8f636bd764a66d36515762d6ce176b27b86b5671a8423dfa39076337d1933d74aa8c5e859a8c415028f710d88a4e6a2e112b530bba187c13d7611320e11f265c1ef902fbaeb409e25b14667f50702098df280b5b0ef633ffe16f1c1398e30c3b4b4232ecf7e643caeefd32b6cfbc0094dab0564d7fd53f03d9861c2a0ae02c0185951303acf07d60016141f517b45fce6dd260f644eb49b258175ccb74364b515d2e644e4e563e31cbe74035397778fa62b870e2ba8e5b515756742983be14f2acbb7b7b856d1e44650471c0db662080904a3454d8b355d07ae9129861bb5747c59950d5a9f45704c3d14e15d9f8652d0c616d6506db3fe685644c78630b47002d6122bb5c2f0d7bfa56014a0b482f0c0be06cecf8255ebad8a7385bf5252f7e86867294efbe4a3ab11c7d68120d756e45721f62497f5c3bc014283575a128710f2a282760b37a4db2ee3af525e64df752e5270e64e95df280a77a68a0581f94097b767ca88831452ef51fabf9f15fc21e4e498628c67113ed5f06452f7f1a3f446253c0504073a574e941351c4668761e8a3b1abbb174f7d4ff535e1e362f456f9016b177e2417ce8b53a7545c546a8ff2212d33f7b49cd72bb3d8630ce2a93226768d0a9f7058fd81f6a63211d666cfc307b2a608266f78ef429d3b7f872ed65e34c1181681c2b81ef3699a8e67ec92c491ea363fc1fa5359b797308685ee85f433c40dce43b5aff2a338c16074f0c59382719303f7ac922d1528fd10174c674b4020dcc041af526b9764d2c547bd62be10532971c2dffc91f05f0ef1c173d1a1d25071ef467ff0d2103210fa5395e04e9668e3c9251bd5a721e316b5338e1fb632c49e66b51b914ff4343f7c40ee8873739dbbd40611020f225f2c0e8264f43873745122c2199ecc634379aee64973f2900d7f7497bd52afe1a8eb3bb092e00105a53deb87a1e5eff5b02fdfe17ea822977549471731df4432b3c0b2e6f5a6dec520214e64f76d35e1a938afa1393acd005481be74176825325009e9e012ee120086aa6ff2d4633fc0c7bbcb51f45accf31d8e2a523bcdcf252f574ea6b2fcad7352f3be913e9416320b228530f3af40d7a3b5a8671cf1ec16871636c0e7de76663d0dfb42fead4536f9374c26067272945cfa8e32c5041981c6236de2d6671a52e13cd3a178c35f41d7b756e41b06ab17842cdf50b74c49d02adf29a5da3a67777d1299935c12bba36ce07551773b8b83301568e15d436035ddf882a2be0f0816948cfd4324551443122f889384e1e5d351c839a73efbdc960bb1f4061c2e9dc281a13b10abe84391ba1aed25a0a5d68714e6bc53d90a1821a2808b719f6d59940016a6f0a1d7555029baf72251fb7da3fef4f4b23406f2f159bfbf242a59246657de9452b6765106200f02d235da68578530cde4e5463614a962bad1c6d135628a5beb84183d7ae12f192b1279b3be44ec86d8c341201382e4476cc30941a4a22dc98e025c1841f2ee3c86a269fac1d697c4d95537a94b81fe1a5933bac18330f1c1bd923430bfb172c8e701037cec65a194cac09f0abd65a12171b0706bcec3a126c5873757ba27ac845164452910e3ac9bd63209066165571f80d77aa84452fd206d9514151d23d95a0c018f36dba304695835209ae5907507c184510b7d878fc4ed06e93ffd6064feac15151fb592d753c353eb68db66b3af5071229922c545d9d832bb230463593134e7dc9cc016682cadb7c7aa5c204e94b5e6f6a33ae293a7c4006b1e41a1d762bf7678597da136dd5d15f9d2ea830623ef0260d7a7e03a9f28059c81603074ac15d6216a5f42aed75a74e2f89a67525c414625f38793b13b07f68fac36d3eabe4b0333bd332113aa1b361e6ecf4288e8dbb3c322436381c025770c562533cbaa699156b401d26a9bc555b8d654b391005423d00b59e1d2862f91d3137030c1dec3c1979edfd7ce084864af6a7593e84dd1976f519ec2877887074b3b5c0022a9a6d5039bb3e6de557dd658be5e5156da4bf3fc418c90bd9fef146e1aabe02bf8d4b47da87a401538acc72a94eac54e9baed1af965a92519aecd131d67790cd3f6f24fece1534629cae227f74180497f1b8221f0bf050f285f577606c08b76f5a7f13600635f7829f845334c94421ffb6139411c0bcf278ed1733d9c184d4a3af2c62fb1e5b275d8968b684446fd2d9341d62ea93f54156ee2fb3985551b3d0523f71fe571f969579752141db8f41010100e0053025d259825c777894a272b0376c222c09ab670322456295818c85da0590033142a681376b8bf37b28d704133f2142e9a0d4c48f7ec1f16c7384d738f3ef97356cfaf48e971452c4a162b671cc9233aebf80e20a6f5ff1b810581091292ca0457f64c26ab9c9b25a2cffc7ad5705728288512759d0e8337c6bec55d918eca4d67fc2a3372874c303de8164e3ff68e58be82db3cdfb67743f38b5f570b37244b5335277dd327ba22cfb25f00f7e0ad7e6d37593cb495b4020460d36523dd5953f311e3534ca3bb1afff274082c66de255f59695e3c65ba303bed76194766b3072faa4319c95bff068f5eff2e66de455ca9b6fb5e15b70d485851c431ebbbb668ce41332930f118788fb14f61ede3e1174be2da2bad81c1594b3fbe7b7c067a26eba5e4643129bc65a5b0a644ac040040afa9a24cfc965e2272524b3e9d757a30b0386a1ae2aced054eec315a4abcbf07a74978351617ab2efce4e44d3a00d96d1f012940987fbf6179bcb801e1ee183257eb844ef9a6b21491a7e70b99f38b35f551350ab363836f96e5f71fa9f3032f0530af79c5770677f3f9eb30f008d4588653102e283f2330db4d247243e99b281f8b9c65eb811f07da6f116672169406c5a2f64aea37ee2bf91d9c4ae70aaf75633ff530bce49c42439452686be6a17a81bd7739d9b40425e7cec6380dd4e52eaa4d43703ed19c42e2137628c1e2b40c68bf905d4177121a63db620b710d4f42353dc06b3033f009cf33e0767961470dc023c659f43ec034dc968347c0d9145decca0534e7024f6df3a7b3034a703d050cb74162e358b15bccfdfe4f4feca359902bfd2ab12656077782b328d74c6c71fd0f5b6a210b90764d56a77d8837c02c414690769cdc3e073a004c7c7bc94e45a2ed5a65e74aeb10d1900d2f84acf00b33e1b54b34af21100dd644092ac38204a1a08b68dc3d371acfac4021e081ff77b526d1025fa3ea3fd72413015eb6d5618a98e32a7cc74d6276ba4c70f3265a318694c76189b66d59ad570764cecb9b6285e26e46055e9c64e79d93278253fe19de22053ef8b1343fa1cf342c3213f25cbcd6500a56153e328f0fe23e137cbf1ba1df4a324aa49d22aa52830fa71ed749d479cc7c3888781c4152250b0d3e9e598dd0cd6be405395d2db2790ddba0482533ac45129398fb6b3ea99125229bbc128040d032fa9210586c95192d91f3dc11fb2db30b05e5cb59cf304143afcf8d56716fc41637879b4d1c2c84306dea024be27cd106e89e6032b9becb45c405922d2c588a2103f2a10d0f88ce5ae779fe576ce3ff67c89b622cc8517b37f8ff8c2314b7b236d8698b289bd4df360389a90fa639fb462716df4d8d998266bdb9445f666919603d03bc24dfb63b7b3822d15fc0b1e532f6a94774ade8821eb7ac146c70337956a9cdfc36a120d52a80c42334d3bee11ab8283f3d524a180d686cfa48c4413b1a6a622a6e975dcf38adc40d20730267691ded6b3ad73168636b69f67d2f66e24f7f7d1a2cd7be0076a159372269e4097cc0a124479cafd7778b367221808cec21e0926b63fe8fe80d7b7a306074d4da5f5d23077357ac140bbfc30255c4d68b588ad1dd5fed18c510e2ea5b70e367c00c24484506edc13f71ce724e3fdf39270fc23d61670d9e2b74e884fc1cf84fb23507a15e1936e61d47881e9646104469673c384e3ef60a3507d423d049d3027951a0b6082981a104630297811f5f799e6b7bcf362384c56e55d664db4dac0f553310a6133f02f18957b685e8567961884f04cd53048e630b687b79ab4e7b09767103ac7e5ece5fd574e063c23ee34e7874cd87a36b53098830b169c8413df02a0dfc324b3328769e16949d2f59aa94ea3d3d57d56187b56f02719b5c5f66b4a5218b192405463dad2015bf3104bce64f399e35914f8c6de17aeddaa431b823d06b47a64910607c293305bd634f039d705629579869d36cea2d13b304397125f950d58e813cf0cbae5f39d0d70391fd3a7cc8b6b42bff34e876e9028d60fc716842d69bdc7df78b2241b4821a019343f259828a686cd003bb6205616a1c387d747593322b3273f0be3ec787bc1b23be1b7a403be815b39f8c60e1040d5ee9d33e74d4d9d34e0881bc2932d5af4405e30c7aacd5152affd7fb1b6ec0a10fca5b697313dca4791f55554f08458a1b3cf4a9215ca3922e80fe9b598ecf8c0e9c8b4c7434f4c220ff8479336060a53544a61f7e83ad116a5b65ae703e4e306bc69ef87e44831e45a3d119788932316aa94b573324f49612067ac16eb51de67da33fd2207f2f2c7254413f403019de2ea6a69d500f28b04e8e35de3e3b3b5b75d13bf0675782a028ce8c043091a5d551b551a4794d72a42e443ea71b06f81f1f58e1656d22c3b944fb613b3cf2322158bfc377271f82f4647353273a5e78041273f3da33b64d9d4ce95fd86013c78a52e8ca944bb68111136f76b178ab27d414f92de01c2d94c876b5bdfa7d79f5357a4b477c3c19f6961f2799535c39d0c44c5a0a263db6830a541dedd525eaca7d3cc00e4c7041c2653a0a9af0363dc4820b09d6ba2af921cd4fe17aff551fe85c6e9a11c45d361ef2194651dd24df925767b7bf0e0fc5177a6b4f61f25fbff44c5b36449d75ca64b40231d17176bd369b220bb0c94496bd487e9e7e3641ff49f924137f783ac4b6e01de9856a45f3925e29fce4954ab0019b288c38eb2be2108e208537bb32869a094cecb87515da8667684bf9ae0966baf55a5449f2449cfaa256db33e151794d9976f6d0a53ed863e66b6e770e48efbeba62170c321b71617c45b9acfb648ca5a2749a4bbf121c910b04a8bb587b05e92a4ae830a70db66bf30944389e34776dcc5110270e118888d27a49fc5c6788d79d1c67e7394b081d2b2be178a745120c1528181ec51520232d677d3d4a1721ed6e3ee439df69428b34288ad7027d9ac9da79a493cb1e126a8234be0f4c687b223345989ee50ca12f5f4bd71b846997b8ce287090636c60551640247be26644e53d534adef1069784d25915f27f6df2b46531f4f3da6277c87b65eb6f3c04cb38c176d616f9699125171b2db81063c931aa77c6c36d370032cf39220450059d3b707cc821ba432d8c8749ad2bf552c61e582ae0c06c260d95d65813839e46e1233f5a04def333cd3e946446373c0cc6342f50e0be2a42aa00467087de6329ba7da6375723280d14d7ef0b5286d91de0700d2e79a5f33c40241e50c247083201705f77e4114636fc2366083cf1702a678d371aebb4a70525074150a160d86703922a16de4fe94a6bf7e2290beb96480e44ca448ce5275e9661167aebe4de60edf1404f00778d5bf5050b0b038b6f570bb58e1254a800113fae1655ee9b8a27e0882d3808ce0534b9aa5330f940ed70893a26041ba739787fe041094428d21eb506256156dbac010fd3ae089d40be2df2fe8945b927182f83b14300224b2c01e0ec3f644601cc6d8b419c60d1e3e06c36f73e5ab9261141caebcf1305bcb562966d2860da70ac5710970208f8f5a66f93d4030b77f63f483397cc030c66992c6b098a3d366815093dedb5684c2c886224ea6677dac1983b82190b54cb5e5a045053d540f93e24352204891d81a6026a886291537e1d8c2418a29d2d5fb62962b32d3b015fef407eb47f39503099cb695aa4a04e2199a762dc906c26747941576c86161275c1ea503b28bb5bf82e66616aeb8930497b6741676c927b818ead3abc457e2545c4676daa502a12017fba5e26c5535dc9abea56a2d2bd3e44f9d46c443255385e45530c7a98d23ed9de8176830e8753334fba3f64143f6beb162e32c9f8aa59be792149a338bf3a3ee74754233ad903ed7bd17a485b5933dae3863881dea0776a1b644ae0c74314b264c45ecd45cd615c1a35093cbf3b7dd3c6394362fb97144978bb3640d1db3917d817295484ca6755d98b3ee8f8f42731cfb61a9fd1601314cf3014d2059b70339ec716f507d93a935b0c0143803959da55c432ee9fc71cf80f150f4f5fcb0fa64f6d251461c971a9fd565aebecc77a1eb53a2fa21e662e4ea05d68ffb9e64193e3af52d5919c0ab618e839c82f37566f0b894522e44003b1bf53457f1a9c10a5450d08213c3b5722d6b15131bba362889ae81c88c5507e86359a2abb985170fca2de2fbbca956e8d749363f70b71410f72ee788957325f11f503513628d91300c28c3199c37d1f7c80a5432115d85ca7b4ba667fc6671974c55f3693a8c43557297a3bacabac1aa5f16a00af1b2d565c42b91cb76624551e0b2737ca49083a05a9551df9d8e64b64b9452e8f1e8a362d1a004dd547b15f40cbf76a4fb72c45b0984e7eee9104022d83ea20d919c051a14e784ef5eb1b5c19ccb520145573461749606658673a216419f469ed2508265d83000f673f9e73b4e65d5adf6f493fcfec900a6bacd905c8aa8a43ee9bce490719a62eb19ec06d03cd2219e40fa042b3e50343459da91361d6616a3ceca023d26def759345d1254e538569fce5607bd17e710f8e11b46337bb1f25cd745b430bf5433f8530987545db1c03c50310637b9bee2ec42fe4393dd2ae122632fb20f71a8f48fcf936097bdfba1308ba780dc9d0e3477567b75f335c7928f20db9329ee2f255356ce43a6c2a68277d293433ccd8123783c6451827a72e4f8d87a52417ca967c0fd65d7c13bc780b649a1462b1baa80cfe3938775b917c727572ad605dd71131262ca05fb6ed9d1409f4ab50b761ed1ae798256cdbbbb526fa0f69613f6e945ee9c82545f0f388610477ea502240ac6a1e1ff5392e44b9034c74fd4c4846ec16ec0bdc0b7993473b027400286b9ddc0a1c60495d8d4977035666b655f31ca72f9cf6784b7cf12767464df9539aa9325f7f1715253d1a7a1a6d456412f8b87e364581300e84392b05c113f9712978c25a85aeca490311d0468716e45361f57c3436ef8b7678755b2826c2bf04e5764d6cfff5994beecc0864b9b16a26e46a0141cf78b32bb301e651ba03a94518cc94588f63b64ff144f60c6ee0374397268f733924c95262abdc1c798394044302225a2ce3b0490401e725f29560000decb50c3b64527432f1dd30be8725734d53706175cd603c7359443dacfdef7be9fce4273013bd1713bcfc792ac56276ab006f102831106e88ef830d78cad55925903357467b1d1dee23ff7eaed53b6f394c9467e5643e5a5725fc756df3d07261688e37c3df115c1fb2f603e2544e2f63e0706ecbe422632afe705769e5143069d2b0266981a75da9fe72204529061b2cd7ef7815681c0c83a1502fb89d0d2dcfea016d2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a94b924783495782f5799793df1def24f4baf9a25d2c5d664830e5d12e379c277c65a4c003140f62a7ad8495ad90ebe4ad431f55cb1039a106f10585f1bf411103732012de1580c70da33e41d30ab5b07adca624c0d185b1cbb7df425ff4558750fc29b610727d6083d309a6fe18e877d5aa2db780ed217340ad4bb1575a21862d4357b22a2fc4f6d72889d258cfca108bed68a4dbb6ff8356353a8219f470b76fae03e260cdcc413fa98e86677ac4311abbc547ef0fa272dfacc343f60d9730972469d00fc7f0e4808042a401908ef1c85a6c61cc653b633e7ac5b5860c60f574419ac21f08a314448fba54f8f0892790dbcda79f693dc57bf0260312feb4d54c5913c1297e2ae53f9120129fc0ec17d7cbcf223763d450e10308109a8fef874d22183418e871914f3ffde0430aaad7379db2627923d4862a8f3272d1c66023f2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a53904064cbbd81310f33564e0e62713dd567912f4a1fc50650be64577a8ad60293a9b91e6b6e6458b1d4a132c0177a593b48a55b49d08e3fcafffe4b032faf1dd8c98e27ccc0e0057bcd3c7da5dda5534638212502e0fa1db314627d5f4bf448965cb22f5d300f481f6cce3ce0ad05378ea4a0068a138166ab7cb43ea4fd0f571997f530fee14c555dfa8a0d7c6c614ae000be60573ca550396a716eb884f300c695bc4248a5fd2d08ae572037bdb54f5c771c2bbb88e30364bb532621ebfb453621614e58b16e4d94dc1d2479b03c53b24b4a55f1fa4a58faabaf0b79e4433c9a756941944cf726723d5651b004f94e2aa0ce7936d4181771f29d5ea5007a09db0ceb1b38a4e73fd777333989e21d63b1862a77d1daed7da2a3c0120db05e5fe9c82545f0f388610477ea502240ac6a1e1ff5392e44b9034c74fd4c4846ec16ec0bdc0b7993473b027400286b9ddc0a1c60495d8d4977035666b655f31ca72f799eca304dac1623fb799f303d2a6911a184f21e683948115b8a7932b19dd4207b4001061a6e956e0679ca43a1481112225d8d2988641b08583ba71246774f5463fd7e307975e7085ada754792529b74db2f1c020a2e212f5893434bc495e400dbc4fe73b33a3033b88dbc629a7987732534d34657a63b1d651aa3174b3e7a2753c43a15ac8e704b805a1d349a88537ab3377d6359d55e39f29f8801fe2c2537885d114ad505a1441fea3d223ba54c3b5d929c3320101263544b3e1ed458ae044eb3f57808430a4f736bd11c4cfbde4d01bf7c36ffa59a21e63dc10de168160052de347e531c4376acfc9a5c1c271c212a57af3907d869148a360e1674ec4412739eb853776eb6796e1dfa29eb031316224773154c40513d2f73a267a7419472e1a1016907d610664c4cf8492eeca6040f3dfa4d5a971e7dc61210065ade195eec0bdc0b7993473b027400286b9ddc0a1c60495d8d4977035666b655f31ca72f86a5947750b3262fb61c0175416d030bac4325480f306629c8230d2bea4e7124f5bf7d6b3d1028046a4d3601817d4579680d3f16db1dd571104f331ee8ca5561f9ee0f59e8786c14c0a71e19dfe0b96e2975c6147e03e63b2057fd2a17d8f02e30847c486e638726da7379080e794f4ee9feb06319b4aa500e655973c2c50f0d8ac1724f032d85070087c70c5d83f3714820e11826ef9c4afb59ab4cfd5f162834757a484a146d608d52ff3a5a3a94144b7a95124e34f114b8eb9a394e8af10776afac642f0b8539a30f4a5e287a1339cb55875adf3f995ba8b93d19382c7b2bef864b052ce3fa16e4ca280accacef6da3fb75450970b376d532b06f0813e365c6be7b27789ebb03c2c4c63991613d44b3332602b38b403d55c1477d6fa6c328d22183418e871914f3ffde0430aaad7379db2627923d4862a8f3272d1c66023f2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a9bd80b4e20c6b4448a74e663295874189afece28b192fe3d1dfb93505206076b7882986dd280c2473fdb556cef8715692cb50864b8ebb159d5fde660cd60b35cc4823960ec304d1fda76ba286b5f7465c43c2006c13fa73273118a0af300872089a5b55eda1e1f50d6287c6983a9a3466c33db5876a92a14ebdecc055a07295f633a7e5f2e06b326179594783640485d02c54a7046eec63e13d8a824136acc02e2103a588cf5b42832bd5e3f8b57b153f2ffb911606f2c441f906032acad7b171454c36be42960487964240cbe5bb8401bb624499f70c75ed6398e28c2b31863011b056947658a6db699f32e231c7e7e2860795ec915447b845b8a6d53a62b4ef9a951626892155baf122630271b6c753abcea48a267be20cf8af73aee7ff065e1a1016907d610664c4cf8492eeca6040f3dfa4d5a971e7dc61210065ade195eec0bdc0b7993473b027400286b9ddc0a1c60495d8d4977035666b655f31ca72fcfd40c5433445a785c25c02379d605114c89e43bb6ad1949b8915521524d433b88953b48e69a9842f3d5db4c4922df474ce5ee5cebe3b539c8daed37cdb10112f1113e44bfdd291f1211b5691b9df91741bcbd5d522f234ff85fe2180934260d9d9f401839772176a7a9684375e58e4666288e5ffc9953180d2ac7411bf4d5081436e178cdbb164b327cde005fa8ed1c45a5aa54554bc66aff461a2a8f658c216b2f0278e6efad73ad7243582c9d6777516f7f6473c2293adb57eb0b179c9e7dc2817401cfcd0a47bad436644a909a0b9cac826cc063765156a2c42428465b384419ac21f08a314448fba54f8f0892790dbcda79f693dc57bf0260312feb4d54c5913c1297e2ae53f9120129fc0ec17d7cbcf223763d450e10308109a8fef874d22183418e871914f3ffde0430aaad7379db2627923d4862a8f3272d1c66023f2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a35f9a6286b6930123202ec35b15ccd7c3c08f0087da9677079f14346cf56414585714444d2ba065258bc480d3fa13e411eff914f7ecd1708eb1349134088831c0bf09938558095232335d30c7248262f604dac54b012a254543ffa66d45195035ed7e23693c3bf50fa6a1f39b7b06f0b6a568604be22027ec791e03a42935b15add1c4508666f61a68f47f27eb10d04b6e5e7d6fb0165c051c234d342431707d1dcf9f6dee16552e8888885176d6b13b7ae0bc7ceca2c11b94c5d31898a997659d2236534392a442ea2a11685bbc885e30db875354f43b58d46f383f8c31eb6f70a743015747be5da237651e6480a03b48c4e964d317436ebcedd10682407a087da7e65e6b4a805ab38b4b0b73942c2a03c0b42a5fd1d857ed02910d1f164b586981a75da9fe72204529061b2cd7ef7815681c0c83a1502fb89d0d2dcfea016d2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70acf1e0e2103f6e00b0594e444eb5bf87c11080c421ba35e6d58d90101962e7c7b4aeb2517565cf56b1780f54f503f300e8194ed5dfeff216f73287c5be711b25e3e6de25a23583b5d8d8a5b4f8c561e471fd4350a2d7f830b65e06418c0111e266730c63a0325b44d975cee612bbe5566194ac52bc86b2c3416335d46ead985124ee1917ee1576c02b45629299c09f04b4f9f2d36b8fa5b624a92c600578f32765c8c180f596b2343a817f150d6b2501e818c11669b02770388e11316f4f0d64be276de759620db2c18cae8395fccf44ba1089f414237fc57a40ee56735a74c5aa8ef3620a2f780125725d077bd052059bc6d7831ca3e6336feb1011c9ef88e10c3df115c1fb2f603e2544e2f63e0706ecbe422632afe705769e5143069d2b0266981a75da9fe72204529061b2cd7ef7815681c0c83a1502fb89d0d2dcfea016d2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a8d4cd743e3300e78799fc2752bbab636ad51fd02891eb26aabe47b01c9f593274a59121add50a05d550f895d797d7a0cf505cd455c2c2347a262944cb8af0b1b4b2b8805fc79b361e04fd119f555ff557a8be36cbf78d44c5bc55e1d025dd76819715357dc8a754143f5962ddc1a7f6d87e03d3beb142d11f66ecd556f26ea74e396c24ac2a88a625db1ca74e28fca4f88bfc337078034598cc9176390580a48d568452e25353a6872bda97582755b023c8e273963477e507533e6141f720d4eb5512e4a4121220bd90c903d7ce3e336b1ef430ac0899107818c4f204671df3783244e31b2092d713f84903c4cee543d2b69bd2f64a508083548d02d8c3e960dc6be7b27789ebb03c2c4c63991613d44b3332602b38b403d55c1477d6fa6c328d22183418e871914f3ffde0430aaad7379db2627923d4862a8f3272d1c66023f2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70ad1ffde57ef8f1043f427fb386b3aee391fafb709bd99556e4fd3ac4c8a333035b4c1de42c83970707d1bb610e731c22bfaf67e13d73213568a758a2f566b014610ceaf7b0f7c872d7b366e2a103f2414a4db9b6a43a64a198889ba23f4ee4b20c6c5a65cee442377abee613f5d28634188fbc43fa418434cb42c1a015d6a613d1efa4c54fa1efa11e598b87deb249548da154a180670f8545a16214b8614f5596a7d7f288c64be0a770556799898db55a5823c4d6c044c3719bd9e46406a6b0fbacec7037cac8b0eff2dbb01446e874cf6cdc64d89661d71c4604557b7ca253ba8ef3620a2f780125725d077bd052059bc6d7831ca3e6336feb1011c9ef88e10c3df115c1fb2f603e2544e2f63e0706ecbe422632afe705769e5143069d2b0266981a75da9fe72204529061b2cd7ef7815681c0c83a1502fb89d0d2dcfea016d2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a6cc23012a5e26c2f28eb7b2b6aeddd5c2009cb00e6976911ffa7881e854ad01ced34fe074a7e665f883abc15b4a39f471d7a9c6c973bec38c019582f28efd03aeb626876703eb37ab9427e422db8ef212526f1052d19d66db34e7863f341e815c32474707c70a913ba70fb5d589d880002c2a271fdb9ec1b90f3de16a98c023afe835b5f4486064da4cde524573d917c8be76657ebd83757cdf20a3410ecaf2bb226fa3c455d9c0d0ae5d05a08c4842ffad0f22714eb956b27e9f92a0f49900cc23d875f138eda30f0771d609247f82f4e23f11297ca4e5af9baba27ea70df77ce50d97946a03f13ec46d258e35de142bef81f53691171270937de73962f1d0bf9a951626892155baf122630271b6c753abcea48a267be20cf8af73aee7ff065e1a1016907d610664c4cf8492eeca6040f3dfa4d5a971e7dc61210065ade195eec0bdc0b7993473b027400286b9ddc0a1c60495d8d4977035666b655f31ca72f22eabd2294f4791bb2c8f44959eaae7421cdfa3545118845ebf12d3db7614f792efbb3031b08930bc2b9b9767f00de7ac34cc22345427a4db5bbf04bfcf7077e3bb6f4164feb5709aeab365a5f4d4629970663654157f36c7859d53b497e7e1ad63b7e11ba258d4c02773c1704a4517a0f58b22f9334aa5a76fa3a0041cbc76b56aee111d2ba6032f9658b4655eee31f95d2b424bc5b2779594a9976408ae73d8fae4621b8d3f20df70cc270a7dc2c61647d45133819970df8fb4115d9d1f05efbb28c68d1a87c733ab08b79ab581f7a3b73112f9cc1e50224935169a47b455b65a0e303aa3b331dfbac26374be821734d9d852da85e6927e333e26872713a0d7da7e65e6b4a805ab38b4b0b73942c2a03c0b42a5fd1d857ed02910d1f164b586981a75da9fe72204529061b2cd7ef7815681c0c83a1502fb89d0d2dcfea016d2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a98ffc50d3689374be3adab54300921409ed6244ab435b958038cb213789a4703e78c3c41f52af150b9414143d3ce166879ca3b6fcd51335b23aacf4abf5a24568c28ea080962cf6e64ef37528fa2eb2110ad0d093f440973bae9764e49ec8e3bf4550669f58cdb078932d56a2968726d1f699e6fed4add553cf5d5613d21b237e89d326fea1cf540c617f87bcc566c636190e600a1feb046b2814d06f4c71c52800ebb4b369fb93e04f0935fd22ae538d429e1061cac994129432a798097ed5a83255d03bbe3c86ae1916f10d79b253fa2449b0c2a766a2755cffc7d6b43006a21aaa45156cd88358bb1447d9a43926966a7e20f7ceae7601086f733c6c0c321db0ceb1b38a4e73fd777333989e21d63b1862a77d1daed7da2a3c0120db05e5fe9c82545f0f388610477ea502240ac6a1e1ff5392e44b9034c74fd4c4846ec16ec0bdc0b7993473b027400286b9ddc0a1c60495d8d4977035666b655f31ca72f2d129e7022940b047132f3102be1fa2d13ac075d34ac0d12bfbb4f153e04ae1760b9120b3efc1c56b0bd19263712574c5dd89543b402a93ea9a901327754830841b79e5865d9c45802099c68ef58cf0a4a81127e8090c978ce3f987b14b044556878b57b9cf7c17551c617111b9f363dcac8bb7ace9ece0efbff717d07277d2aa188cb16a36ab32d0af1d93c189aa33d06fd274a355e056d1a5d704e0964353435caab49a88f35568ab52c013ffd2c76ffd0e701e1e0a1261cbcc806a218c2059d2236534392a442ea2a11685bbc885e30db875354f43b58d46f383f8c31eb6f70a743015747be5da237651e6480a03b48c4e964d317436ebcedd10682407a087da7e65e6b4a805ab38b4b0b73942c2a03c0b42a5fd1d857ed02910d1f164b586981a75da9fe72204529061b2cd7ef7815681c0c83a1502fb89d0d2dcfea016d2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a82632e38a27dbe221a0dc4726de547283edbe4085a98da149e182c65b877b806f5bf7d6b3d1028046a4d3601817d4579680d3f16db1dd571104f331ee8ca5561f9ee0f59e8786c14c0a71e19dfe0b96e2975c6147e03e63b2057fd2a17d8f02e30847c486e638726da7379080e794f4ee9feb06319b4aa500e655973c2c50f0d8ac1724f032d85070087c70c5d83f3714820e11826ef9c4afb59ab4cfd5f162834757a484a146d608d52ff3a5a3a94144b7a95124e34f114b8eb9a394e8af10776afac642f0b8539a30f4a5e287a1339cb55875adf3f995ba8b93d19382c7b2bef864b052ce3fa16e4ca280accacef6da3fb75450970b376d532b06f0813e365c6be7b27789ebb03c2c4c63991613d44b3332602b38b403d55c1477d6fa6c328d22183418e871914f3ffde0430aaad7379db2627923d4862a8f3272d1c66023f2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70af481ac66d78f4c0593c7a504421e912a83ff0017d1e74c60a53cce4f95772f0156447b1f5148af549c190508b5890409012c974b4f16bc202047ef0ae59c8364df8e88406fa49464ec7240167873aa3e2f73d81d095f946463a2c66cb8dd2561187f6d311b902b533df34b1ea107eb4b1492fb42fe8b8c00cff13a2091d44169b40d6879e791ea6074674e70b79c7a117301f3019d1da974089aaf2808edfc0a05d49348bb0e5d03374d686e8e68d330f4c0d97032b2e315f0aa574bae753f498fd1ab0c3913e243af6c2072e9c47709102c9a6006d94c7b553378411fe2816daf9de44fd487a741cc671575b32ea149b8c4375400ae735082673433f7dc1b29c5913c1297e2ae53f9120129fc0ec17d7cbcf223763d450e10308109a8fef874d22183418e871914f3ffde0430aaad7379db2627923d4862a8f3272d1c66023f2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70af619727df0145253fbb6af6f273b166315fce706dd7ed5230c7ada00b9c950271bc3bb6ed78bf868e653a22ce257464f18960b64c31ad6040c544f00757a527791c08e18a3dd764f5092535f03ad192daf28e11277d4fc06d8aab5605b3cf07c4553a34fdca5837de8bfb82a53b4427909773e7203895e5b8fcc5a3866736f029ff4a65f2cc0db633b93af2e3daf7c6bca5010237914976c3c13b570b27c002135caab49a88f35568ab52c013ffd2c76ffd0e701e1e0a1261cbcc806a218c2059d2236534392a442ea2a11685bbc885e30db875354f43b58d46f383f8c31eb6f70a743015747be5da237651e6480a03b48c4e964d317436ebcedd10682407a087da7e65e6b4a805ab38b4b0b73942c2a03c0b42a5fd1d857ed02910d1f164b586981a75da9fe72204529061b2cd7ef7815681c0c83a1502fb89d0d2dcfea016d2af354735e655e6472ee426ad6094c57361050514a278f64847117750d42b70a02f4451a71727c03d082d0665ffecc619761a06d5fbbce5fa2626e7df6487e26c2036e362e1b0266c7fafc5e426bef0ab9190648dfd71f6cc6acfb1cdd962b3cca96524f38264916078b152bf06cf8703bee7922fc633564e1518428c97ce41301f5b44f56f94a32bde63d3f9a05121d911cbd0ad00f750c7cbf120a003ff72389515e32ec228e1cb0dbff7cd93ac25b5a855027a13e86252c65632dcdee113646ac8873a0bd643fb043a57a0401473982323a0cfc3cf10766eaaa728df5a50e9bb1d541153afb566bb78b0e59bf5a57d6817336f7ef4c618ead1d727fb1ef1d4b1b393b8c6c152b84e5c8102428105e955809079404ba088a5c167753d32b0933bb283c1d68240eeaa7185ca500d822ebc17a383b68cc47a1667f5d799b730dc26c5935d065a379c1744c690027f70000000000001792f14c32d5af499ef16e4e9fe29a3d0629f7463602743a1b6d8851" + } + }, + { + "participants": { + "data": [ + false, + false, + true + ] + }, + "proofData": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + } + ] + }, + "proposerSignature": { + "path": { + "siblings": { + "data": [ + { + "data": [ + 53456573, + 137628195, + 858995544, + 889221965, + 58106200, + 690630578, + 503640118, + 1136171460 + ] + }, + { + "data": [ + 757354853, + 798490044, + 1612933346, + 976747799, + 1058551263, + 477122616, + 1593895659, + 826344671 + ] + }, + { + "data": [ + 715255955, + 557904687, + 808965264, + 399317068, + 886972942, + 501498739, + 1548037812, + 1958344690 + ] + }, + { + "data": [ + 1334332038, + 2130355515, + 1527904489, + 779761642, + 1115458327, + 1364518939, + 1761357686, + 454740423 + ] + }, + { + "data": [ + 1993415227, + 1181255890, + 983001521, + 875758073, + 1044064753, + 890262307, + 2078142816, + 106066673 + ] + }, + { + "data": [ + 1698106378, + 1293667713, + 1972090158, + 1133260658, + 747742242, + 1690596251, + 1932093814, + 1650647722 + ] + }, + { + "data": [ + 204659119, + 1907343487, + 725115491, + 1901999186, + 749284501, + 496648149, + 266197328, + 156533658 + ] + }, + { + "data": [ + 540779659, + 1490247663, + 1781782453, + 773134842, + 687743615, + 826574374, + 197959659, + 462604392 + ] + }, + { + "data": [ + 1302316477, + 80757537, + 406345381, + 1754132194, + 713914762, + 2091625278, + 1009880070, + 526938472 + ] + }, + { + "data": [ + 1082282777, + 1391643778, + 1448533113, + 1251781284, + 640806861, + 2036513049, + 1068899061, + 1149517070 + ] + }, + { + "data": [ + 1770487503, + 1839288882, + 1261728663, + 838784783, + 1589303172, + 140550884, + 2013444274, + 1485787540 + ] + }, + { + "data": [ + 1579614307, + 148697335, + 774660765, + 1817788627, + 735277441, + 1764484336, + 435729503, + 703445910 + ] + }, + { + "data": [ + 564164474, + 1790160203, + 1517676614, + 736741607, + 2127071162, + 419220311, + 1618741473, + 1492940469 + ] + }, + { + "data": [ + 1088666437, + 2049222529, + 1498153548, + 1190805262, + 629530984, + 1786200236, + 1385999425, + 719640225 + ] + }, + { + "data": [ + 21727314, + 687193213, + 2010022237, + 222035039, + 969170542, + 1677916464, + 1413195334, + 1119362098 + ] + }, + { + "data": [ + 551572258, + 638513579, + 1927624326, + 787206042, + 1899641765, + 1589051407, + 971020788, + 177776251 + ] + }, + { + "data": [ + 1272882496, + 785623149, + 2087961513, + 390520767, + 2064740747, + 1317668065, + 576034549, + 655492905 + ] + }, + { + "data": [ + 142924712, + 44707858, + 1173463854, + 34908795, + 1514092776, + 562978567, + 1630132851, + 1004732858 + ] + }, + { + "data": [ + 608263883, + 1778444568, + 2063704548, + 1398487111, + 315845679, + 342032978, + 1928658168, + 1556084346 + ] + }, + { + "data": [ + 1973699606, + 1955229407, + 1841161171, + 1413126838, + 999486358, + 1367134607, + 1327918261, + 1021403361 + ] + }, + { + "data": [ + 545584425, + 638066383, + 215787119, + 189079755, + 170035825, + 1120218035, + 441959064, + 482670668 + ] + }, + { + "data": [ + 1752364911, + 2012833620, + 2031520624, + 214552087, + 965592389, + 388110784, + 828154871, + 597554057 + ] + }, + { + "data": [ + 1841573416, + 2021587855, + 1479214881, + 583282112, + 1292918378, + 206410916, + 1311153763, + 905807050 + ] + }, + { + "data": [ + 672179522, + 49350415, + 973067841, + 1765798822, + 1642566156, + 1664361238, + 1487669478, + 782651242 + ] + }, + { + "data": [ + 223980471, + 556187278, + 213714785, + 1893601876, + 645188814, + 1472053843, + 2104276071, + 1502554743 + ] + }, + { + "data": [ + 342707103, + 1340424675, + 715988683, + 2049077666, + 2022898108, + 614705645, + 1155129217, + 166573751 + ] + }, + { + "data": [ + 825980312, + 322597069, + 1903034593, + 1507344233, + 1585333029, + 1407306053, + 417092645, + 1368318219 + ] + }, + { + "data": [ + 1268533672, + 1484982254, + 337111803, + 1103735278, + 1132019353, + 1485575410, + 1590984406, + 1036077776 + ] + }, + { + "data": [ + 1442609382, + 1743983198, + 1339077449, + 1678227370, + 2067395502, + 1740606032, + 573681357, + 1372034795 + ] + }, + { + "data": [ + 94566418, + 189214442, + 814595250, + 1093344128, + 1276292859, + 907615914, + 1290319159, + 756667511 + ] + }, + { + "data": [ + 1205375812, + 1403882777, + 692837181, + 831454225, + 212378556, + 928916474, + 1653991069, + 1775060873 + ] + }, + { + "data": [ + 1161414147, + 2005670453, + 514628183, + 999475908, + 1267877781, + 80246436, + 1853046035, + 1571110620 + ] + } + ] + } + }, + "rho": { + "data": [ + 970419906, + 933002871, + 566890175, + 365404695, + 53522719, + 1375417797, + 1730152694 + ] + }, + "hashes": { + "data": [ + { + "data": [ + 1465662447, + 245125680, + 1209136188, + 1382751791, + 1250524827, + 1269010043, + 2080494837, + 494855998 + ] + }, + { + "data": [ + 550054489, + 1745178934, + 1638600540, + 864485198, + 908365102, + 2000959036, + 506096558, + 1017108378 + ] + }, + { + "data": [ + 1910979862, + 113074228, + 543627269, + 55974850, + 1756423892, + 1090470949, + 943196358, + 2021526738 + ] + }, + { + "data": [ + 1100740011, + 1418315338, + 529740160, + 239948523, + 597464305, + 413726553, + 932136683, + 1047274345 + ] + }, + { + "data": [ + 1018929428, + 1249927688, + 629172403, + 770444785, + 1115156955, + 1329621436, + 1422798195, + 2121283851 + ] + }, + { + "data": [ + 1547328623, + 1040879126, + 543641658, + 1758670467, + 1380192552, + 433570367, + 1733649774, + 196528021 + ] + }, + { + "data": [ + 1984562198, + 1298493706, + 1369276541, + 867618626, + 1551282782, + 14117681, + 1234628507, + 412816063 + ] + }, + { + "data": [ + 2020017277, + 1807752805, + 499573787, + 177278343, + 1261417916, + 1820164368, + 848822463, + 181852444 + ] + }, + { + "data": [ + 1980618425, + 792944134, + 438235386, + 1851014503, + 1355874079, + 200373891, + 1765004321, + 170607529 + ] + }, + { + "data": [ + 2022551753, + 1560068848, + 790592226, + 2051524133, + 408238241, + 1974204060, + 1498029473, + 1860979650 + ] + }, + { + "data": [ + 1147968466, + 1913492388, + 510170387, + 1183780497, + 350155887, + 1820681951, + 291957044, + 1487642228 + ] + }, + { + "data": [ + 2054322434, + 1126184112, + 879817373, + 1845689168, + 1627181812, + 1256118496, + 1264734028, + 1576808706 + ] + }, + { + "data": [ + 1453694280, + 1724283124, + 703248619, + 469037137, + 1020257283, + 713562993, + 2060803498, + 2091177312 + ] + }, + { + "data": [ + 931833433, + 1807479547, + 1439200721, + 737093548, + 458145826, + 858020105, + 1923465393, + 1611465450 + ] + }, + { + "data": [ + 144209541, + 1468248729, + 535981540, + 1692495632, + 1533932736, + 1781768964, + 710796816, + 694939373 + ] + }, + { + "data": [ + 1388549805, + 700435066, + 115481809, + 1625649025, + 948109451, + 736545754, + 408119131, + 820291442 + ] + }, + { + "data": [ + 1550283458, + 1895579585, + 1757066391, + 1941601104, + 1615794423, + 1032664603, + 1074602158, + 505303516 + ] + }, + { + "data": [ + 1949914537, + 103489688, + 224253409, + 1653351510, + 438634934, + 1842972199, + 696959471, + 1056277961 + ] + }, + { + "data": [ + 961325721, + 1603435058, + 873204113, + 795300062, + 1353725884, + 1017846336, + 1508940480, + 1454191042 + ] + }, + { + "data": [ + 1617794274, + 1930189167, + 254927345, + 2057356112, + 1527998259, + 287540012, + 641552125, + 1992962643 + ] + }, + { + "data": [ + 972039708, + 1225091162, + 107007087, + 1987396422, + 91745713, + 2058618890, + 2046633241, + 1532567233 + ] + }, + { + "data": [ + 771782273, + 235012673, + 479619060, + 1517198460, + 1478670063, + 158686200, + 442687790, + 1579838854 + ] + }, + { + "data": [ + 187322795, + 1048433843, + 449001401, + 66845821, + 1733955118, + 746311989, + 3158910, + 1922304833 + ] + }, + { + "data": [ + 1356423630, + 1033137407, + 1262859394, + 1391695773, + 936148667, + 201661347, + 595862419, + 1473204909 + ] + }, + { + "data": [ + 2027005328, + 168156857, + 510963576, + 1790068780, + 125802734, + 177287760, + 1262872574, + 891720340 + ] + }, + { + "data": [ + 1467540442, + 512549700, + 1460807465, + 251532755, + 1160233628, + 237918738, + 282716952, + 861152316 + ] + }, + { + "data": [ + 607947364, + 245150463, + 1744124095, + 1160965731, + 1108816400, + 1953158405, + 1337883161, + 1725006323 + ] + }, + { + "data": [ + 687679511, + 1998603739, + 1250555137, + 883426403, + 897580114, + 38509834, + 495077344, + 190293290 + ] + }, + { + "data": [ + 1000944848, + 1760068469, + 398121731, + 1530452432, + 1081437803, + 1296013896, + 643063736, + 1333981280 + ] + }, + { + "data": [ + 1176148587, + 1253745976, + 633706525, + 558028827, + 1711298894, + 1913180524, + 472827106, + 1766459385 + ] + }, + { + "data": [ + 604840959, + 1978481524, + 1326084913, + 2085677361, + 1648086830, + 1385137058, + 161767904, + 1614016132 + ] + }, + { + "data": [ + 692300450, + 367485581, + 856453821, + 1827848420, + 2113920761, + 1484501484, + 91287561, + 1948996203 + ] + }, + { + "data": [ + 455225542, + 285581819, + 620415379, + 84877668, + 2023163011, + 1732618595, + 1033406762, + 2014717376 + ] + }, + { + "data": [ + 1051392827, + 2106464079, + 1083388349, + 579818488, + 1241485100, + 1238757581, + 1232253951, + 965885056 + ] + }, + { + "data": [ + 890130934, + 2002057150, + 643140464, + 1947402676, + 546173342, + 1850324925, + 203843177, + 580593022 + ] + }, + { + "data": [ + 702151147, + 1506972279, + 450953541, + 1563588650, + 1535452300, + 1645858133, + 60816255, + 1346328846 + ] + }, + { + "data": [ + 702859673, + 1130271164, + 220818706, + 1927913764, + 322305552, + 1743220717, + 1546866099, + 667430872 + ] + }, + { + "data": [ + 888397036, + 59863928, + 183477363, + 562939586, + 32528264, + 1047777012, + 206058289, + 539780155 + ] + }, + { + "data": [ + 51141174, + 348789716, + 981679370, + 37572197, + 1614560975, + 713631326, + 1311619932, + 675515752 + ] + }, + { + "data": [ + 1452770196, + 400208770, + 1242267007, + 1181349267, + 1830149241, + 1009354661, + 371342382, + 403157846 + ] + }, + { + "data": [ + 832687736, + 787015931, + 876382364, + 1561507381, + 829041746, + 517430070, + 1825159104, + 1085321542 + ] + }, + { + "data": [ + 157186566, + 1903794927, + 2125152246, + 1399648160, + 850185739, + 480811075, + 809647436, + 1468233501 + ] + }, + { + "data": [ + 463332849, + 1487999938, + 1766476367, + 648909384, + 1161110337, + 928118129, + 1654146576, + 1144416468 + ] + }, + { + "data": [ + 1097197651, + 1022141399, + 1319123391, + 1406744870, + 1085693360, + 794902552, + 1484755320, + 1147987933 + ] + }, + { + "data": [ + 818590595, + 434652242, + 1547729825, + 1086001145, + 1449163909, + 2075933479, + 559710398, + 884223332 + ] + }, + { + "data": [ + 2087185333, + 4171136, + 597528646, + 118766126, + 159686969, + 828631798, + 180536985, + 1659108615 + ] + }, + { + "data": [ + 1494617084, + 409321213, + 1156573875, + 1364908121, + 134566831, + 579165408, + 1090997383, + 2108007913 + ] + }, + { + "data": [ + 903030812, + 883922342, + 1887085821, + 1131936248, + 184318562, + 237963369, + 1666115847, + 306343499 + ] + }, + { + "data": [ + 1550325563, + 1830305475, + 422553098, + 1129921119, + 819050490, + 87729651, + 63454563, + 924469667 + ] + }, + { + "data": [ + 603694215, + 1757204856, + 1573512884, + 1733316952, + 892691449, + 40841626, + 1195404810, + 1290467739 + ] + }, + { + "data": [ + 1579823888, + 820084889, + 626691614, + 1912175849, + 1792845119, + 2065199879, + 1269916503, + 1297863632 + ] + }, + { + "data": [ + 346826287, + 1671302485, + 1682666276, + 246026500, + 160258471, + 54981049, + 703203396, + 2126484981 + ] + }, + { + "data": [ + 494829706, + 1652772295, + 1076660939, + 1815867502, + 1942949210, + 1501544550, + 550794363, + 1737696254 + ] + }, + { + "data": [ + 1397518281, + 983454309, + 1639350877, + 465047068, + 652080302, + 2046522386, + 1053610645, + 107787845 + ] + }, + { + "data": [ + 312736452, + 595742660, + 1741564027, + 224467647, + 1085767080, + 568258613, + 121851840, + 1475413311 + ] + }, + { + "data": [ + 754371758, + 1271777668, + 110125072, + 1485464683, + 1031832662, + 654437064, + 2020753944, + 1207619964 + ] + }, + { + "data": [ + 2081805667, + 959630107, + 1197370547, + 608229331, + 1720509351, + 955210417, + 1216464148, + 1404786142 + ] + }, + { + "data": [ + 775338304, + 1683567535, + 2081694540, + 2122298872, + 764068520, + 153632047, + 523910248, + 982258950 + ] + }, + { + "data": [ + 1176196844, + 1726510120, + 1366918543, + 920875298, + 1997105927, + 1064380993, + 1914154307, + 351879759 + ] + }, + { + "data": [ + 1109094795, + 89383595, + 1653154652, + 1121159498, + 473868978, + 590526365, + 1527088554, + 1119815472 + ] + }, + { + "data": [ + 1337237053, + 441960684, + 1842405628, + 759035653, + 636534435, + 1355330028, + 1435196384, + 1590789126 + ] + }, + { + "data": [ + 440354277, + 571949236, + 2044990505, + 1734719904, + 169284232, + 410871032, + 646837277, + 1660940171 + ] + }, + { + "data": [ + 1378151595, + 1837788202, + 522855657, + 995346982, + 780827224, + 797011394, + 1761671369, + 1481536736 + ] + }, + { + "data": [ + 1147076439, + 851460847, + 758389214, + 151900844, + 58683188, + 309137250, + 1302063605, + 735864402 + ] + } + ] + } + } + } + }, + "expectException": "AssertionError", + "_info": { + "hash": "0x5ee0b9a253c6544f92af173b375d18bd3f4888dbfaa86a388788f16eb4945fa9", + "comment": "`leanSpec` generated test", + "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_aggregated_attestation_signature[fork_Devnet]", + "description": "Test that invalid aggregated attestation signatures are properly rejected.\n\nScenario\n--------\n- Single block at slot 1\n- Proposer attestation from validator 1 (valid)\n- Two aggregated attestations with different data:\n - One from validator 0 with valid signature\n - One from validator 2 with invalid signature\n\nExpected Behavior\n-----------------\n1. The SignedBlockWithAttestation is rejected due to invalid aggregated signature\n\nWhy This Matters\n----------------\nThis test verifies that aggregated signature verification:\n- Properly validates leanVM aggregated proofs for each attestation group\n- Rejects blocks containing any invalid aggregated attestation signature\n- Works correctly even when some attestations have valid signatures", + "fixtureFormat": "verify_signatures_test" + } + } +} \ No newline at end of file diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_proposer_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_proposer_signature.json new file mode 100644 index 0000000..d5d94f4 --- /dev/null +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_proposer_signature.json @@ -0,0 +1,1268 @@ +{ + "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_proposer_signature[fork_Devnet][fork_devnet-verify_signatures_test]": { + "network": "Devnet", + "leanEnv": "prod", + "anchorState": { + "config": { + "genesisTime": 0 + }, + "slot": 0, + "latestBlockHeader": { + "slot": 0, + "proposerIndex": 0, + "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" + }, + "latestJustified": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "latestFinalized": { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "slot": 0 + }, + "historicalBlockHashes": { + "data": [] + }, + "justifiedSlots": { + "data": [] + }, + "validators": { + "data": [ + { + "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", + "index": 0 + } + ] + }, + "justificationsRoots": { + "data": [] + }, + "justificationsValidators": { + "data": [] + } + }, + "signedBlockWithAttestation": { + "message": { + "block": { + "slot": 1, + "proposerIndex": 0, + "parentRoot": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", + "stateRoot": "0x38d7edaf9c08ffb285eb3e1e64456fbe430d4c4a4bab9b0bf29565b74da5c620", + "body": { + "attestations": { + "data": [] + } + } + }, + "proposerAttestation": { + "validatorId": 0, + "data": { + "slot": 1, + "head": { + "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", + "slot": 1 + }, + "target": { + "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", + "slot": 1 + }, + "source": { + "root": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", + "slot": 0 + } + } + } + }, + "signature": { + "attestationSignatures": { + "data": [] + }, + "proposerSignature": { + "path": { + "siblings": { + "data": [ + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ] + } + }, + "rho": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + "hashes": { + "data": [ + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ] + } + } + } + }, + "expectException": "AssertionError", + "_info": { + "hash": "0x57eeb8cc34ea6414c49fb92c5754df75e45aca5b655142f30ef95e2dc1212c59", + "comment": "`leanSpec` generated test", + "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_proposer_signature[fork_Devnet]", + "description": "Test that invalid signatures are properly rejected during verification.\n\nScenario\n--------\n- Single block at slot 1\n- Proposer attestation has an invalid signature\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation is rejected\n\nWhy This Matters\n----------------\nThis test verifies the negative case:\n- Signature verification actually validates cryptographic correctness\n not just structural correctness.\n- Invalid signatures are caught, not silently accepted", + "fixtureFormat": "verify_signatures_test" + } + } +} \ No newline at end of file diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json deleted file mode 100644 index fb3b40c..0000000 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_invalid_signatures/test_invalid_signature.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet][fork_devnet-verify_signatures_test]": { - "network": "Devnet", - "leanEnv": "prod", - "anchorState": { - "config": { - "genesisTime": 0 - }, - "slot": 0, - "latestBlockHeader": { - "slot": 0, - "proposerIndex": 0, - "parentRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "bodyRoot": "0xdba9671bac9513c9482f1416a53aabd2c6ce90d5a5f865ce5a55c775325c9136" - }, - "latestJustified": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "latestFinalized": { - "root": "0x0000000000000000000000000000000000000000000000000000000000000000", - "slot": 0 - }, - "historicalBlockHashes": { - "data": [] - }, - "justifiedSlots": { - "data": [] - }, - "validators": { - "data": [ - { - "pubkey": "0x2c7aa65d0b45c01e3ba7155997d3001613775f1e1b85fd1ab65dcd72c65e105f2552c43a311a290af56fa0262dfebe10745ea837", - "index": 0 - } - ] - }, - "justificationsRoots": { - "data": [] - }, - "justificationsValidators": { - "data": [] - } - }, - "signedBlockWithAttestation": { - "message": { - "block": { - "slot": 1, - "proposerIndex": 0, - "parentRoot": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", - "stateRoot": "0x38d7edaf9c08ffb285eb3e1e64456fbe430d4c4a4bab9b0bf29565b74da5c620", - "body": { - "attestations": { - "data": [] - } - } - }, - "proposerAttestation": { - "validatorId": 0, - "data": { - "slot": 1, - "head": { - "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", - "slot": 1 - }, - "target": { - "root": "0x6f2ebcd6e5eb1b34823a5fb5867ee63984905cc670722a01a060894b9b2cec3f", - "slot": 1 - }, - "source": { - "root": "0x438b1cbc01c3c69b14f8853c6463d28b58118798f414f3be472aae4cd77dd572", - "slot": 0 - } - } - } - }, - "signature": { - "attestationSignatures": { - "data": [] - }, - "proposerSignature": { - "path": { - "siblings": { - "data": [] - } - }, - "rho": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - }, - "hashes": { - "data": [] - } - } - } - }, - "expectException": "AssertionError", - "_info": { - "hash": "0x8d97e6b6a601e10856ae70720ffad8cd392dab8169fe158054edac0bc0f0e49a", - "comment": "`leanSpec` generated test", - "testId": "tests/consensus/devnet/verify_signatures/test_invalid_signatures.py::test_invalid_signature[fork_Devnet]", - "description": "Test that invalid signatures are properly rejected during verification.\n\n Scenario\n --------\n - Single block at slot 1\n - Proposer attestation has an invalid signature\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation is rejected\n\n Why This Matters\n ----------------\n This test verifies the negative case:\n - Signature verification actually validates cryptographic correctness\n not just structural correctness.\n - Invalid signatures are caught, not silently accepted", - "fixtureFormat": "verify_signatures_test" - } - } -} \ No newline at end of file diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json index 5d5d3a6..a40c7d1 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_and_attester_signatures.json @@ -120,7 +120,7 @@ ] }, "proofData": { - "data": "0x00" + "data": "0x08000000d50004002f00010000000000be78fe416bd6fe14000000001bf7ff64dafeff25f2feff0d2e7c29022906d430ed6eb2416f07bf12d01e6e0954c83b6b95023b142293481ab8261b39f7b4657d95e2be088c3a105659d0f6565d936e2bfa9cb175ed360500609f5e633149104dc4454126d7288e34a0a26b2d992c3e5709a02c2b6e52471d7cd254412621a979ae63da120321252d4311c1033c48f368d48b1006864f12774144a52b247d71570106a30211ed6139dfd24d080cbfea4d02658629e4410655f71c3949310ab81f213d420567a4fb4362363a561ba4714bf4a9431a3cea72129d42b205449f353dc610a22a4c590f79caee0373d0769362fda2c640f0aa0e12f3c6e72fce5c6a271f073f5b66f6a73a171d913290e0ed6943dcc60c05be0a474e8eb14e0fb4c842472348050b5d862be39c4801239ce812f0ccbb08c92fe3791a655a6d349e674f4ae6273ab31f340f45e39f07e4fb8b4f2313705249a5b94d6e634a1b1b76dc710205f96c09fe7d00e655f84960bbf71eeb64ec6c0e3a985813d8343496e50042dd4a7d7501683a09e9a79a4df7c5ba3a8bc7a9573ab99f4e6e604902ab0ce82bd88af17ed23fe0788897cc263730201d6d0c473bc2a53279e2b84e01f5e84935e4e1b36dfb6e3675ab147b40858dfd25a8b766250224fe7c5abc7373e78ed160e483f938dc13dc2d7baeb26e18190136078cca2026acba3e16283542d27e337cb39c854b37baf71df51ba5497b7de67035b83709e1acc83066e68d00cb9725085799e763f892221bcc17d425de0f2e1ac0686f4541946a7768555769f097d73842047428fc18561b31acbd74dcf1761467ff01459b771873c328a52f64c95255deb1a63dd0367c4475f0001a027121008abed41470cf545a00cb19164108d959e797994531932f57b2f0c13ec68215766f31265f29bf5e3fd41e885aac47f333b043b50e6c84b74ad8c08e0cad3b346a14dd5e44407a592047b6240d028eff6749140e279ec7396ee5b4c65bdcfc283471c17a3586f1e93314270949096f2275ea7b1e50d394d0199bba2762c449702eaa848021c9df98711662bd4cfd1e563e3d677504e7f7355f824f182adc4c775fb433ab4cc6444e184c57780c9850282a01d7e07725e25f6f9fce523101adf70b1807e6733874232df0e4e15c2adaf50a06812e09e3a2476a13f5685d7715f255cbdac7641549633895ed6918a8335f03cc7e98023533155dff8e8e0cd904836bc597bd72630b8c78f7d27304ded23c119a954b43d0fa8d100af141045b8fd0670e3c546064bae1094ef45b3c0252bd26f9c55567127d3b1fe37f2a675e872e1aaab43705ab2ad4447753d175d8e8786bbefbe04dcb0c276afcb49e6f27458556d66f4b4fc5341d5fc253dc43a0d23f1a854d0c557e0c67320dd1760ba5bd797bdf8b6427d20ef163930f80059cd9c44e0645375b46d16b28233a75468fe4a40aff113402922c3809f76b296cb4c2237ef32b6b63e87333752cefbd5f9200b531da96af19ac166957d7d9e411ba47686ce574490240407e41e9c1ef41b4f2331183aab6784461c364cd61d85f7abd5259a1c0441887017d7a7ea05764a4b568683c38d3301eb1556e0d1e2078117868276d46f24c7c48a35629058e5e46baca74b90f3a7b747b3521fdb94d1d233cd85f44117700eab1fa2ffdc4e10dc649c61aabdf9839a2ae2c5d75830f56fb93057594f9203cfd1f324fb4042134875a761de7d50e14125a187d31506206e364701023cf9d11d3cda0062ea01a0336c457567195eb7d27fec76431916a53128d8e0b97e91814d3c9235d2b9be969fdc03e038b37476aad8a5534ddf9611c84bf983b7fca8073b816397c4150c25253fe8959a841b4416c1ade5393fd305e66211664a6dbc52ec3889623b4217d43e80ce45e66510e6c5c03811afe116d0c3ad00108c325105c81503f371a25b945cb4a9307e501a844094ba655658da2354e926c713f7a562185a59650ff362a2e62efac4ef07f7e18b135fe24e21fdd0a8159f8177205ac5791290a5fbcae424437afd36f92ba805639cff564ada48c32ed52ac0ad9e99528ee498f5a78bbfc49f9581e7293def375dba17022949ef24c97112e4d9ab8de25b0548c69e4107c7d1da16e4dc573d604c3f6bd53a5ab8b5f8d229c0fab0b5552d61e95280f1a73098c3bfb1539b7ee17731236421a611950371dc715753c7a00bff81777c44a967618e0e602b080862d5f390308a31f5f4a0cebbe319220b85c21b5f21f0cd5ab242d5f0f7ad5676259535ac46f664fbe557618fc595e95e50b3951f37ee15dc356b55d5771925c236321d4d66ddff7377936691b39c1bb097918faa309e932d24af26d75525c59e72d0729085c93534579c6d0f270663e3937212dfb41256ec523e9d9b87138dbf6333c867d37f0a2a9697e6e511e34545051c5f8031561a61b580d576a2c678cce148c5b2034f25f1f01a16bc00741e6e57c12765b13fa94215ecf0ae806a8a7a4651285653a3573d33f701aa6606d5b3862e33e1a09f2d27709c5ffcd019c329049b58d966b078c5815f7ea1d15fa5dab16b15d5e135b83230d5d4c8f7ef0e803375a14a85fc8113b2019162c432bb6804115bd3663c0e3931d3eba60397bf8fd5f3d01fb5621dc9c5549aeac0c6de42c75a40cd02f7ee1545eed7ab51670c4a55e757d2f4eac6e2e38bf38ba300106c8531fd56459a1fd5a73b06ba90676db2625bddaf6705b413a16c1dedb3e46de9349056b9d77f99c0832ca20fd4392ae9d0ec651cf6a722d104ab16a2705a5b769447f70997ecee1a03fb58da918a453ba44939cc307b41c1d0bc1390e46ec84d72a7a89a1070f193665304d8f27c1a22a2bde048a49fbc33302fa582f3c813f2d576da19e5606b7540444a96c19904dc41512cc1e1ec3840c21f974806af94eb20aa86c2d7c7ea9ea7db4af09746e3eb43c710861426dc0eb6cd481b74074b35d0410fa06044534ad45a9d73a467dc8fd6701dc5177ab1a4d46e392cf7d6bcaa45c1a813f1d710fef09ad47f74487a2914ac96ac16e77d4226a26521c1f857904651eb1257ad1752435c2f3ac13c64670121083f87c70e2d91b5b7b621dc048cc52ff644a5c904ddf169d303e415921d94c091be37a7632920f1c018b0da572a17d02cee5172f076f688fe4246afa55ff69f6775817faf9ee374be69b5b8deed80a1b8e192f52f7b90e8ee20057f686ea08b65b102021abce2a61e1f3504731420cc2426b5d74fffa6137c1f84c9090b478c0ae1370f426aa11a241dc466b1c85550f68db423d2edd6907d91d37df7f150cc47ede2447f8a14860c1452143235e5228c7a12a9c602c588180267dc9a94f4d7eee221c5e47703c24b8377ed9a4261fcec19b057ff7587c17bd9818e8d4b975eb9bad05c8046e24f5bcfb6353abc554a0156b4efd42d32d826d982973f2f551cb18891d647fc61ceb569d2302f7b47426ea5a3180540f0e9c48120d0292704b4b4f113b0ceb911a2e4cfa7a959d1a775057e50d230d8a7db7f4774edbcb4f204b41015d7026b02e9e67cb327879d709c3eeb126ce912422fc0ac21095db061ba9c45d2608672d77dc80546881194d2c8a533046ca9f2862642d9d471280ed67c22f3f7a95e44810691301308fa4d712aa84c42ef9246367f1e9af74821c100795f8ea00cd147f41387f0c618d74111e1904cc0030b6935b8956814a0bfe240d265e5b0473b02043ceafd6413656a0265a531d74baaf5d7328b0ce4e23dd86574e449d16489fec19f1308a0741eb905570ccc40df6bc895eafbb7f4481a3103bfe4d7629a76ff8449e42f10522e10666c3316f30dbf0140818df8111dfcf7216d76fec604f92bf0fc38d7d3cf6bff1798d5aae4610a5b77aa30fb573f052ba6f66c79b731747655524a2795f8fb55f0ef83bed26c013c57e9d829f52c969d5088c253c1a758c1b185d134d021387fd5193a30d3f1a9cea1b34d012515fce78281c911e42c39c870e04f9170afc42634c784033271a16bb56cb94eb5d5c25ef21f113036e26bd732019ce1273a91fc3694b880e2922a3f1156696487e5c9298234071ad18ff1d967b9b040a7a33d85e00d1a3a21d7d8d9322c2b6bf3cbc29e048dd3bbd0f9c99042661d030591beb6e27fd172d4a8cabc4519ec3102ae01af8164e159728501a8c0ae4be134ddc8c667e0c1aff743a1b8c54cb106e674f80ae280de27a794adbc8539d017a7491c0ea4e6730a34640c3d1546a261414a865087e5f21d70564ad5c0e861b7c7521915a28f4a1376788370f5bac760954571069731f6e41746d06f25818d1e5334b052a09d1a8603a64a8db5804c56e424c75160439f9b47df32227375d1c796e7bcf487d2b0d4f68242d872563df3b3d88e0451338598d563f56f474ec15662abca1cb39e1547646d9914d2275a2745f241aef0cb88b327e0f56d15d50de97094a09557655701a1a03c0115afea8a6174c288b5db76f816ecd1c7b718df36447d88b963134e9a6479b319a6d1b63d744d9ea6f475ed06731facfd63da21958147dac0c729265a64e318c3b49d86bd233d442fa744f1f4c5927dfae01c412ed770d552b31f00fb72b177a89729ad34b74c841ee57d3132171ab9ae9351f6a71548c91200636e3df37756ac826e564de7da04d4f75d4adbd144488bd7d18f7a6431f2544611cd12c304e836722e8a12d18028ace4c58c72c1b0d706f2f2970d40146a3d574d2b2f355af149871ccc67701db5038323dccf94f7776db5cf7ead91e0dd11c7600ab7f49d1ca0347a41f20058a2733556e936129dca75a319f694f1dc1dd983636d40a7449411b3ab19013568faa2a0cffd9cc5db6d62a4aaca91d30bd528021f4e7707b5a8a7b0d1c9667264ce71a1ece16ce54fcb57e609d15a319da48a74d0fffd857b4033a1502548c0ee1c1324ab6c91e0366e9e81999cae419dc0d2065f708db0119b62a4ce1fd18315ea8f83de9ee5078801d1d44ed868d69d2f8e47edd7ae73453b9c46e9d3bfe618fe05762d7aa5328a94fe5159b72567098f29a3c9113805b922fb51e3faecc204881a37b30c0cf2c09199564e7856d669c9a907e415b3572848ba17cc35d6b62c99ae52c4463be4d9eebe143f3ff5f6f61aaa60582f0792d2c800f47a2d9a661251de54d3ae2634dcf7eed27cfd97d5167cad77842d6bd5ff5dd9e6df71f9b692b7e7f0e2df5151066bb946c9a9ba01811a2c1388480d063e2646c5cb8cb9071a2db644ea6663727cd374e23e36e0e690ca0631088600b10b0d162563e72575a19a2b529c422d66913219a4c728de8056b26f55b28e6274bb394834f8f866e5e63b88c0dc188065b58183d1446dcb7510801621a250d456662986a4aea51be1072a14231d9eb004a92467450c0f8192df5e38378e6581c000da7c738d379ac2c78b25600be57be4447cf9f54d4131951ff93343293840430b252a04dc44d855c27dd3b58a5cd732107489e2eaf797e48f4b881029aeafe2a2b7b9108676a647efbab826dba805e786cb11d0c2eaacc22a5bd694c59d88a4761cbd76682d5074d96878959e15648115e933c6867518e61ec60c92136e6d253edeb534ff6ad0f2f0509753f8cc54f4fa872cd43407bf345e24aab2b7a7bb01d4fdc0906ea0df6120f21dd3e119a74693e69ce51e127d2462db7556aa662e07da36fc7339cb82a30d7a866769db774617710ce286cf2a2300a585d6ee058543732aeed2e83598150016ddd6cb1b2446ee338f6556593550b4a0dc90abad3756cc0d9d563dc362a397690497e6178c4730b77b3163611963efdb9722041cb5679381dab5cc7ad6d596ed0d22fef036c6081be503450f81443e5ce34235904880822106a2399bc7b4ee4895e0f830c5d7a923579452f7a7009faa3734ca6138d1722f4514e0f946949e7ad9378ec9c96739ffc7850f7a58c36357c676d3f94713f1f4ebc6480de9c609d02e61714252755d9afb220205ef96426bbe96789863701defbd119a372ca28beaefd7a8386b670c28544414f3c575f93c2d0734da3472660f4b82fd3014679b2de80072a8c0d490a890774d7d07c38b886976d5becb4286a89523a0cb2444b37c3561c52eff659b6cc0b52f17cd90f0a82d95d0856b60e40d00f2df8a32a403ad5a35ea633ba1aa07f483759bb0f09fd2b4204bcc0e0245f48c858c833597d33886e1b2ca0594f7b1cf812c9534918425ea75baccacb71e79aac3cb8365948f35fa44d2b75c130a3048e5507d8ff2ab4dad74522e986561490944aa04b3f0dcfa2925a93e1fc3fef4160044356013234fccd60e761820abeda771a8ee16d6700640251ee12af61f2242537f3299e30ac087348c79393631b347268c9316554c1daed7a72310b3f3450e460e07e9f02ae4fa70beda9f02066d8ba328ef8283ed670fe18de6dde0c215f8325e22e4e0316398156981f4b63a5085e0b8044a62c6bf0705f162568526296c87100a70665f559073f562d30037e9b7e5f8046ee55775c7a613fef366e0bda8669350c2711606f7219240d7930ad9fe6550f872a3162db384eed518e55fb6d58118e492c21cb8c2a6e806f0423d70430760b0fe37b124bfb6ca51729005d66cb1402c70b10c45f921a4733243624c86c6614ca575759568b1381942963ff2293687fdd9c155503c06f0972f17a02b4c240fe582d571fe4a75e83fa8e5719850c1e44e8687d5bf1c105cb76a824c14ce1713718f00f70560c18dd444154f84ffc66150f0367a6d47a08d1056c194f19854e6118dc77a9961d3a9cec343eef616e66ce451303a749662a2f2a020f293a6d59b002e70a3e0e9003a7794a2424cbe4710538b97b56445014504ea103ca522029b8ab254e6cd14931d936b31a6deb382e773bd70415bd3a6d10225513950ead6528f70507349f2d0342c2e70a8ec4b0237b74d8163b32344cfc8deb60cec6cc666e0e2670c03ac87dc1873c058deb64749de05813a0bba328575f8d3934ed8540cf07d70f8b636d6dca66492617376e541ef0680697aa216e3cf6b32042f2d71332bc130d20e34746e1cb13472f72d037e186ff6e874c0c1544e9dc5e934e9f366868851909c3680ba627486960857e13c4d21f6ff94a6216626e4d294403d51a79250f6f23b6f72973df1574bce17f2d2e09a95c99562a245e96374144354e7846854703b4004c2e3df14e2db944cf63f761cf20ece65426f21b8f22051b12131ae070492a7f6101523fce1b886c18494aafc53c22cd3e5d56107f1f0c51b7520778c762d231830d0d81907345ca461a497c670c5e90f7703891343c57176128074b8c0481c1314a50cb9412ab31c930ed72712bb742217d6ace4d0a7cb6f3123854103cad5c643e7f7b3412ee36506d531a1800f0a9b068f4b0471e70136d30cfc4ce3341308d0a7a81d94c807d8553a382bb6763e2681037c48b46722af44dea0aae48238f3243b4f3f21b183f53194de7430829a9a95a58beeb36bbce995c82fe7f2ecff66e27d9503a0973e6b60e47b7567e8117da42d10f1512c36a4c02ed57e5112e967d0c26844a7274adae1356fb9c2baa75a90d915d5d112adb68016d7941531e44ac5db1a1b611234e7f771fe1ef742c1db64a8eeec617913c4132ebca95608326d6367d4baf61281309155c7c15736a30ef78614a806c2e7b9e3a5b93e70f0444965fa7947e555b26d22eb587d45e8ffb92445dcf8800544be310dad4156355afbb4550b1e055fe2e1b1e83caca60dd6c641e05548e569366e30e144edc2a1ba3901989cebe501e99ab4ce746603e00ce957b167dfa7408a37b5272ef6c14ee7ead762ee8320c34f42520437e2c2cffe9ee6e7fcb16613f4a111dd871493ca711d1796d44552b4a54ca2fbe8f5453725a296eb402dd2ae617fc135e9e58749af4ae17927586297babad56a69f6d6f5b325b477f97a11ca36ee52c55f3ad6bf947fb018a26d531a8f1051cf61a811e6818a628b13961359417152025320430f8e735395464051799b66f2597b1473a3253b226fb3f594380a5247a416e0a178b414c67ac46a937f685d800713a26413b43b100c3e6356152a6fa1a0ae215628e4aba7060282374fd7e90451fb0982a3c81355199460f1de45972547355f519db2d71761c56e65bb854370f3129ad2a4efc8402fdd5533f1a04703add6c474de4ea0b6aeb322126e5fd0615ff8cb5449bce560ddb1a922ed2e49371431d013b692c6c54bc85b1730f21a51f9cbe687e4dcaf200e0980258907e563a95611e22279a57422000b27cdfc5b57c38133c306528320d4aa0213632607a17fb088335960e10207a16aa4d52a3213d56a5a43fc367b263335ff648e6ac145d9e9058732cf0315df7c6317652c5cb1d8eaf4c58a4019514d8bae80df26db55cf1ba9a10ca963c2e81da512f1c21f640d2e67c1e276d4d2de8cc1478523a0a1ef00fc26c24c35d636cd3205e639e47314e30ae677ca116022cb5030e7c02c870259fbf4a7694623e053e0a5b26a873081ba06b56e562696232786e195985623aac4fc41f98f0a11f7c915a05a3d7a9278da55329073a8d22d6ef660f1f6b96408a577c45d133905aaa2a504966a3c43e5d7c35756c38f1658498b038f104dc37a619cc700bc3884527e8187c8aa42d2bd092523128f12975625dcb5e2a5394271bab3a65405bce5c71b6d121ed71fb4ee0cb5f309f49e30eaf78251ee4c0124fab1e5e7d99bc5065c8a8b70a1af709183536de35d0d31f540153c070a6170e5839dfae011686654a4a9697384c80770357c16d106ee745057bb92a289270871a11d0a115204e5f1d6dec395f76b42130cf07907505ccba4039d0766806e7512e6815da61ca4e16157b12bf5b409c695d7ef226108ba573469d0e8079c2ea9d27833e215058612114e0d22d1784f5823a8a13387e958dad0bfe25c956235344251ff2181bd6e81b1e623687426d2fbd3813d9ca7c2e25d21300cd9e329baae2363e58471de6f1b51bd44c1c210554652a5c51d171ee705824c564a46d5b4c2c7051e2775a53eda27a99850971df9cd11da44f4e0994afbe5693905d53e05f1437f8cc8d0304f9ce696a292261652090347712fd14ac16710960e6c02c10325d7e301af330af415c28349606495b4eeb6ae610b13d40e22727e8fd06437dafbe2803d990530381f92c794236337f843e33be99201540a6a842f558b545692d287dd6750a2ec63dd10284efe16c3f23ee507b2d4569bb76be0edb43f959e12c003f3ad15136ce50ba1b55ad0217606bda2d95c949558b09f90bf3bd775daf169166191262034ff0b53f611faf7e7f2d7d1e11cc32104a22f47c9157895727df3f28e1dfc160111f0243f2efe1660dc4067cde04b63f4b87f471ad479a3d61e18c7ac337745b2e7f5e2d869599706016905e6109dc068f94e70f78b770055dccb11e163f172d32e2d02b422db07320793163d464fd38b7ee065a4e722e52ed3c294e68ccd54862a34746a79d331ad32a8a797fd559603a8f865ae48b3a5a5225f33c588fd342db686f481bae3b158a94120cc739dd1d542c762044f1270a35e3f44dabe7cc5d7275a8452882cb6cd27ccd271da1770cf36ff61271357e5bfe2aee2866bb186733c503340084ac42f68cc31a113d3e78cf0811365f8b783db7c520735bb3905fcdd0ae017596d401a75da053907fc301539ae66c14454554440354386058f04ae3508c2dbd36ea6b2a6faa32a447277c931fc121b74e0e188392473b2ec4821dfd55bc11db5f5161358eee6c6f179a21837f8f30e834964c4b69206cc3f31f4eca5be15e917837743e4e332ec8f68060a117ed14d969f7424079d5556addf158c8e23315a4f53f446e949b4535d7853d36e2fe48d37c471dd7e17d5fe79b2208bd1f895deb9ace21a2edf176674dbe659914446eab7ff73029d2931cea6aee08cd768d2e81c9da3524a2387449674c7ec8b5057ceba0691752e1152525de98334175a649cf96592cf6c16765621daf16551bc313e142b067a39fd25e78d6e263247ba106594d7f045dd26b0f32d1d94dd13e915197422f76e3817613daf5b94e5c44011df303486f103338385f886a3a49a9650c087a876553aeb5394e773727dadb0850f52895497975f965dc8e825b995d374f43da152ab1f4ca5f2a0c0c6fefe1844e5a146570b5553164a3cea55e4971864841244771e166c02b1a5d593ede7cdf495272325402e95504d263477261e3e2597690911a91b91c77ec4d8e2f6014e14474421915ad49f01b1c59805d1343516d6a9166261cf95d2e760f2b68315757500ef8e2373509c269abe029136978dd0cc579a5666a8b5159c0c7fc5b7bb86d749b10df487d69e55d3b59f52edbe8900d59fd761b52ec986569f676384b9af66d37589d3f2768d667c090253527ccea713a272555b0654421ea69c02c6e58a17e88457e78821e5018c1e9d724c537d971bea2aa215b9a2b1bf17d940e56d522260e7eff1cc4d9941858db3569550a5f17ea89d2709f53a16bc64c8c049f4b8c4347c45e1e86af99162149a941192a6609fff69b02e9fdff57b2993d10e4e508742b04e2001307913bf6fc8068f97fd247160d0d3e8c245362d937ef1a87fa9c18106a3b04a545457b2c40e83f6b571a302fb069466267373c485458387a84ac7861d8e401a7f2806113594c37a2a1d9430d48c81b005a5d23a00b617b20a64c4199a5060aa5e3ec63db185b42b71a035a1adbf80849362237714d376797a483355e6fc614390c9f188b4ea45e2c336c2cf844326439525847a53692784922fb10642582591d10005cfefac820e9aa2a52caa7ad39fabfea73781e4b724b23e57bf486cb32de8f1d24d6f8d97203d4554b76b29007ed66c80d4e3caf540f7b62414cf26a5f019d6d6b6c4b7a399b168f6443622a2716785c1d3aa6cd4b934fdc4f69c22906ac629c26f4987d7e5b89693c7491a2020d71fa5c1b9ab0138a578459c801ee345d71155b7404f7797459cb2cd061bf2f1ce224321c2cb235fe2e4d0dcc9dd14614cdb94e05e1c36a883d826997d592796377e64b8a97464befc6721282db8f6eeec5a02e9da50e20028c7801ff9d00616e60cb0cc2a2871c442675624dfa89056ca50625f8d28e564d11d030ec283426c2be9c35ee537228efcb9646baa25a07440e812b62eea619596c830b6cb3001b1864171ff4a28f2bb773cc53c8316f6dfb421d799de20523b2f51a171e3ca7713d1d082039464153cec7d57e19c0806522503d416cac9e4d946025553704ed072c602a4fb9807400fefbed7c7caf564253d082196100733bacd98963c34ed6655a5f6528787da759414c2161a60eb018faa6094e0e9504765f9a997df84c83102bff03289439887071ab847956680624bbccac3045f7b247134c1427869f007a45868d1b6c377e46fb04dd4d294d5f15c5959f35cf561c46d7bad63b4fb4202bb1f82a6996baaa62492c203a7496a003aad723141074337bcefde5759f66643001db9f358c18802a2c9c6b4bbd17bf79174765138e9ee044281fb56f38bca829f8dc591b40575623a8fe2d1ad22d8f428339bd424044f838771e0a4474ba79512f58843b2074be455b08011322bf10111065fd2e0687c8357ded884de083873ab66bd211ed741869fa5fa07bebe7bc6282fadb5bd5232602653b2e144ce9672a89167354348b0d7d825c7c0163a9da6a90ad284e1f5e8521eb993c1015065c3766264523df537f52973792166c7ed742ff98df704b6e5d38cdcf4163d71b315f8777485a3cec162f5b085970f27c1c345dff8c6a120b0a41fd176b087bc4170f08d5af373108ab03f18a373c2918c643ec4dcf35ae548f7099d26073cda36d4a7fd0465067cd0e6e288d6f05a6b0222a40915f73ef332850fcd76f2abea3297e7f055270385c994cf8011f519226a7047643e07b6a5662473d5a4e29ff36016b3037c12e953a5328936b3a33aa136133ba51c01181411d14c1bed41bfae5445e0413b0710639d6055a2e775277a1537ebb374273c190f3609383ed46d10df1068852a45ddb8afd2f64934551c808f70b50c33e5f50144f0e35076b0caef205157e939a56bc8c6366cce63057b395307825cdcf7896bec84fdbeab24c0ff1f77ab7918a3b6f137f743d456d5820e52b29c8156f427880716b2d7cf92e6785743accc9701ff3994a72a03ad556c7fad27aa374220cd5e65913b0e509094a2c1c584488f448229527744fc88372fdc2590853d1e80d992a9f730642cd00d59a6d5af8ecd95cf397ef4ebc695a24507dba67e9b6906a322ca23867b33c7ef1435308fe27c46d65b0e575725c1d01582e5318a06aca03130d016802c11c3b9eddb064eb268e4b43b0621062ca346bda0a753bd2451738c131e25eedc6fc673ea8626a08043837a93f0f104248cc00ff355b14ab2618236789d14a7fa7c5359b2d400976ceff28aa7a5c3f085fc54578d73c184d99f92d0473f935310f1528f29005296736685818173320d9412f42aab55252a9dfb74990335a7ed36b114ae542f35cfa642b0155d4080607b7b633a7cffb568e26b700afb8b660ea618d4499b6a84c57b7426d71e8cb62a10b01605f8bdf708e43fc408d91290286e9f37265ff060300f775179466b764079afd611b4edc56d644344c1ee3944f31cfcf7946cfdd76850cfd54b0c7b0620dc2b213c1815e44577f014b5ce7b20e2f85904c9c91cb3254bdb869b698095d8836521768827015a505ee78ddf1fa14d913fd33ec51300684bb836a7e37fc4468bc2c45ff439742270e246592a9fd4219802a597a5914526d888319abb9476ea833710f0f20f1408c7a80772e8eb426736b7c62be891f1e2439f857bfe31b392e8dd47b546dc943b9a4e56970126334cbdcc81396f14457938d8b0e6a54c05efcd55a6cbaca1f6ada157e6989f8db3e01ce500dd85e7f2eedb322418d4a14752de84865ff1e264d119ffb11854b022dcebc9525bd7f30000aec47560477c7172320a9306c917772b5cbd9670ada715623b6d66438872e75650a39072ec0f81faa26ac4fe403554a60b42c238a22d34400810d6747fdd61eb93bd24bd1b83660c8fa6d0d25be2c401aaef43153e459637b22185bca94884b8555b5699b600f614118027a4167aa7c9f6521236275a622fb93be0482c97466c8b4d15bce70961cc436ef4054cd7e2ff8b9f3696a058d3aa8d12b32aa0eb923f6f72773e773873df91445375e5fe565c43f7b3bd4f99d3014976d5142f4b14905b425095236e2668382711cd5a82b3694780c474b04c15e00e6367527e7a8394e710e5c872dcc61f630a548fb3f284e76ba2a0c1e6c276551b673240c39f90218142a63d2d77c3085a6da50a36c8f429659204b9c1b004b86998a5ffd6e6e7ebccc5f485300607794c3a879c3371a1b3472eb1f728ee944bee9446f741b333ac0eb5c6e52efa715d55fb8279971773f06d4565316b2830e882afc1374f75f509d45321093d62418e0079861f9942547e968b95f02e40f6dc1bd13091470691d36bfc019e840d253f21b9c57d8a3a75fbc03d94902ae5063da90c416d599f82bd4c75d462f45eb05881778286148347188b02b1e399d746fa978765888b08478f6635b062d133c64ed52270b10745c4c5d4b0823cfc07d1045591577611384244fc5b8347fcda30bf74d8712a0892a450db39a46f48ea144cded29722279137a85e6e434df1d9275bfaced26536816105c1571798f5ef720d70c333edbefd04498ed213d36a6e529684ad724f269376f46434120f81fe0443f900515970165429777d07bd4e2195e1383ca316e7bc33f91f6c109007e835ebcfcad57efe8b64e7e7f0b7946e41c53d3440b34d978de19fa49045ed1aad314205cff593ff3ef4db0c6b91abaad224634b7bc71c7257e056efc4f35cf2ccf387c6ccc5dbbbb5248588add18453c765c5e1a625fcf22ef6a14714b1187f926735e7e417bc60a6a28f780e0302d24d530215c76632cb73f0c61b82d28698392771edee01499ea0b798334bc6b35d26a49203b4c6cbbe51814eac39c75840d9370132f0d47cafe6a253b3a363b0476ed4c094f6b3dfe2d68565acf653e8817555cb65f4d37d5d58265a56299138581ef32e86a4b19fddfd514bccd1318232b9e775d57a040a07cf42b7d812505aaa9733a3c2ab42b01b1ac287fed9b15cc450e33055f18549217165d31c234423172996c267aad5934171a5ec964974079e10f344f8c39281afb942f4c8f771a468c4a5046b7fc2c8a20d92b100ff104c1ac262f8b16fe1099e5db1260571b034793fa11c137c16c35dd1a6f3fd7dd5f2b50c579292b037577c05f20cdea36154223ca393d76cd3f218c51688fd6033823d4fb6e7cf0b9265de20e2a2ccb4512a09ab64222f2974d61a3b74c2cda8b6e4b11ec122bb1583fac94b362ee8c5245d2615d04d747c66b939b611dc5a5495f2f6c226bf7a1ce338fae73513fe1a25652cfce3a78d7970584f4bd211e24873a4435e61fa6155137fd7f8c19ce3b5c6173e75a429d486d230b40f81aacff1c33c14b2046a928e52e07968c5570575a5a6fd539225a1f7009a8dbfe78fbca1076f52ce61307e1e40adbbece718c8fc6712089401d6ae95a4460ded71aae42ca73a9b5d840d3060d2d5dfb2367f348300d6113ed459862457181147674d48b7664ba71c24b8f1bfe0316ed1e4de7bad27742aae53b2fc2205000f8ec4dca2c16128999d16df7ad3a2f5408167151f72d1700923364a357543d89791e2a17f6642a84bd701b70af933fa1baa76361385b45e23c8a3d9b86ff67c1bc4f261138ea4c151b10719882f1016d808d1bf46a702a08ada7132ec6423c4639824137c22126fb90317afd592f2e2a9d8b4701ba15474cf50350842424105feac257f62b1b3135e562248fb53f3d4242a6260e9ae777a230414be4005d4631935a4175b63f0755c14c26a344894292414b469ad4510c06088963b9323e11d5c39b035d2718748cab6a76b318a034da87245b89b98d59ba3f5f69f663e0293a70244813f2af346d7513762074492d843605588d12fd181bca3c2f8923bd3c34f9596e059431107c72763fa87c56250c8188024bbb917477921843085c930612c877604d5d6e7bcb0159103635e324f400c10e3caa0427906cbe6b3b05364a93584d0222459829dc3e8d7a0f9ba5363341ae2433912e0ca8e9a41a202bd047411f857aaade81749327582abaf44803a39f3e2d8837cf7a1678190408f0c56c476a0b707e26922cc5669b5e989f7113083a0c1db56a274afe41995e67ec1d158bf94647e52f97577b5c827508d438336c5afc0dc7dc1968c6afb53b5436e16e5738be4072bfef25538de91c65e67751bbcf3a06b236b76ef1811e77b82c2f2cb2a8da5efd590602061da0083b283c281a224b30aeddde5d1920fb7e7f147f0a3a23cb46157a2f4d78c925037918871c1fccc72806a1463b4000c40b081f2a61b33b0173a5cc22392dc35f6d6274e212d1d76a6a75ca34463b853e7cb027be64cdd2721670a72022b1c02f7ba2e6ff661bbdb37b48bff7760d3dea1df1011148a0b75e2dac1ada33dbc0294579093118b0037668cd26011c45e17c45c552bb7eb65c63757962903ddfda6a11d8330f6b1987790883382c5525841077a0d0b8449ac5e833b1f3072f029b2060855da9073ff28e29fe7efd2883d6020a9dde0e65dc4424757874be4036f9c475e93c37044b740e4aa2dd8050c5680b6b6cb7181e389bb2646a28c659465bdd58607ddf07f6e11f185637142b11ecf71eca0c7333c3ebe1793a68b34af02ce160f63def1aeef82434a361b445b1facb3141e3751359a75c6ddd84bb6fb3a8f25948967e3dfc27c7680afe410440b9ec79a9dad3099f98a60d866cd075d99c3f52586bbc66146a9616c59c4351e055d21e699b944052f29a4fc01ce80490995b32d26f1b17f1603950184260656398fa46ce46fe300fe6b9665976d127a0d2ea490bf57343867eb67a3aaa243694a1045c4ae07d39ea656502ad5ea17e0f881f1a776f53026f696f25cd1d612413fb3452091adf2881c4721c37a79e543e246014a1c593676b467a27d1fbf63e5941286a11426204f6decc35eb6d7468b7a720582e80f63aa767796497b3725abd4f705f692a8d12d9edb01437e47e2e5ff7933b7b49cc661491ed6a539dce2c1eabf713d9ffb5703aa9927b705d44604cbe036c049dca07ca179c6ef9b75c05bfe4e159debe955cf0d55f104c28bb73ef34de5982ca297993c25348aee7f012af455a0cc8d89e243c16a949844b9f1187829c7eb69605190c76804a74f47b55ffc29f5f83e4583b05df04190d918a310b483051b1f89474f6124e2bece86047c7e2e86a57106c490afc4e1628aa7e76557e9436de7dc2793df2d000882fdb5f4ac559391f906535f2f4501d2916c849a5da715a5320b14fc589ea4316e7a73a2297697b5cd4d47ec85e5b4728cc56220995146d73249c0c6fd4f17b9ff5db2f96a5c209f4114262cea5567c4fbd3e7c26e30256066ab41ee477f456907719019b4311630bf97327470fc055cf7f2d4f07fedd60047f82189ef7f3530392bc484ad39b00d7ca6a5922dc276160ff300803c8ed32b0723a56b197d8761634bb1314baa97b4a992e4d21814254a668286c7814a204f83dd77d07366f7e68a4f117de4b2a4877ffa454bb4fde79cd5eed2f88f92c680e82a055aace664e9abadb7d4960e54d4f2fd368607f156469adc55949b47f7e568311200b21243846600027bf4682094cc8864c9f2a014f89e35f6500fc236cc329441668f8e81b58a3ca3c0de736414cfda81b3a15a06c353400255aef952b5c16db3d94945c3c5038b37ec74c4b5fce4c2a11925ec92e3720de0d7804863594eecc33f8bdaf09551ef94639116274af25bb404b5ea36c32b299504822fa204499756fb2f64573c6737209899153385349801bc696b011512a9f7eb2acde550be2052c07d7952f5de2ee27e7282b5d2bddf226e7c6d14cc01c763aa012c7301af44b7b2fe5a87016fb3846a63acb0fec224933fc973f73d9f5de501c9f4f264f04b44491ffbc595503212110cc570cd90e4715252e3a7bb10fff02b7e5873fc2725f2fe5a6e11605a1b05c62a66450412c2f24a630a520ef7a1c364329c210891f2d0c409ef54149839f2516894d27d909ed481e616727adab21488d7d613009ad452395763003dc575e2e0929d233456fc478c7993e5c40a6656cb57eaf3c734ea012800c37290a479319bbbbdf555ffd3876b900c32167a32829b972db465cadc927f7b7405e049566599f54a8312baf9a1ec308bd01a7d6920614330e1f3fc8cd273627532c56d3b839e3c428216d55b2479f8b2868be41c465ab09247c2aae42211ac98151a051ba39bc5a806f2ab57b2ba0e64740ef7e3f5518df2259936b7525bc8a9965eecb1448b568b8219b640664030d801c563fd748060c1c5e176c1945cb94323677f4f725e98487227ed11364e643bb113fcf3b58b7e9123ef5176264bcc1a11350fe7222a971c05ce82cc63b54ba1946935f6216687d90125917bd639e39d270d7d95e60c91e0e4cc63a5269053cf37e699d030eb803a735588cb928f7e6103f8b3a193aad934336d107894f55978665f3c9995419f9997eaee3c24e1f7ba60b74495f4b57e2cd375e93c778dff19c14d7dc0d5cf56ff86f1f7da958e570fd17e6e08651a9170879fd5f3d086f3aca1a71e26d09c20fea2e5175d945be7cd1750f04c73562d1891c3da085089177f348fdef4f542b49b55daf08ba405c63ab62d90dda770aa6b34ff8384a6ce678705c7807e40089f11030625f4463d374a74a3943836524a2d57a2f16ca7986bb8513f787341b01de115c0bc3ba5b4a08c02b1eba965f035c924e22c85a156d652f65c7cc184f32f87c6700056940b4cc0759ec12f71a4a8d6176e5192d42ad8c8e3226e6773d7a75337db26f810ad76ea61c9cb0d765315422003571813b5c60e41b5e59722f829d057110a7455b2097457dffcbb84e9c75d25cb0fdeb6d99d94c75b46a26143584c055d7dc134c0538125d4bbe796d48ddca521134720937622a052403c1350cc07319327757759a3f6609b44dba49c034f6313ab5381fc262701bc695c36285b739236a5077631e83cc56701702251213643405ffee50f3858a65ec1dc2036d2f943008d72e4f53ed9e189082f61902b76f4a3eff942c766aa5658e36586222ea1f6eb0067f7ec8c6df148f94986f2c445a5de14cfb3b98644a16468183423177e70022d2b8621aed437ea2438f4bbcef7b7e9865f9363f5b2f479fcb5672568cc9092eb7b42645a5357df23ed8068e8569301cb4042dc1c1824098ea2e79911b5f054d6a001697302112ccfc0b621e41061ef9c4e777a737453b76e38b7e43b51c7a2c576f3776fbec1511150d245565fb587d3f1368b248f10d5e4a942ce3b1c50052ec595e5984d30cb2913859f26f57435620e54a57c0d842313ee370625c3f713ab73b69ba661c3d08b7b154e28bf46476450e63fc6e0d1045b9c535f6f9f36e777cc404601a576968ad9601bfaa2765f4c7603db3d83318137313507c1896056a56051102738e3d4c573158c1b9f36539a1b97ddd70152b2f0ee978094e28755034016ec349b00fa12b295075c5616d27ae7d7b12eeac16890a1b6421ae300af20fce67c7222c298a91a76cd8b3127de847222d4a18cc1f463d12387272001ac68e725253a29354910c3b68dc54db7e414f2f12de81514dedd9c06191edbd3194607e436b631210302d0456f9a693303deaf871b43eec6175657d2d02a38c088ab9db2a5c97b001fe782668d8ace34b0bff412e3ea1187e93d58c07300a973f50cf977ba0f8fe703143c05b1f56e516a3c4f84f35a1283653360d14ee67b40d4e8ace073c6ce31fd1708a122cfead4254c1725fbae2883bcd35cb346e804b7d309f5e60c07be76948ea4b4dfbb10318a71b82486049482cad491420b10987068c538314117cb80bd87b1054962dc50bae3e7615ab67be0ef8ce1364ac27110089c0a500a7a90b066c36ff2af98dd84e2c37c545a2f79f5add0c2246bf26b064cd3c7a012e1af2184a3b944b8567c974fd089b69b89e4a002b095363cf357042e33ea5201548fc75065a15187c63f9361f27dd3a0ad61763be40d14e70b28322f340b70f21f1b56c1f48a17a134ef51e8c5b410e0aba4803d0505050b1862771731c970b04a23d71215c85185ae1830fec43061c2babd11249ad4846831f8b5d7ad4fe3b7cccd45078f8113b9c19203abef3a27ae202e87e7a6dac2feee8d26cef5a040f6de4fb6af8e4c77a834f1c0f130eef185684c3310dee061c25201410d8b5aa7d2023e067a70d434da031911b25cbb348ef2f7e10388ce424c89e64219d5bfc5e0c97163aefa8dc6d088a692951f65b5294846423ead7484075575d08e14eef782d23ff64491e0366751f5415cc2dbf4c62b72c453dd46a0a9d88707330aaa610d7e30f1690ec4247b7aa0d64a7955947e33f57253a9aec0343895e5c34210d1ce81e523b69f3ec7e845d192a7060532bd093c8053a51ef233cdd7848754505194792354e91ae6d19fef04560400bbb258bcd144ef5f767714c45d76a0269653ff6968a6ee43d8b373b2cbc7be809c164902703483107b1317b45e513c33eca7433cd7f44ff917566562e303ba20d1e55a19bfc0c90f2f66423cc531ef5f1124545481b3e5579af7128d8d807b81a5b0368408a2bbe031c5625f76b0bbb225835460762304b0418656332750ee0125f5b73974964219b6142fa9a2f453664b759b5a65275fdad904bc72534718a93ba4e40e5d400f4c3f726cb981733939c88645b4c891f7bc03743e810de0a70d51a35669643467d30d766394b8562d66c4d3b6115ae7a3800613cd24bf34d8fd8d33e75c72b231329ae50a5030c1a2fac494096534139e2339f0424e5b50ca4987015de3c0a7009e4480cb439696f287877548c0b7c7bc8105528e4292365dc553a437669347ecaf31018f1b3e4501462187786de8069bd1df33a813299126a3ae1367bf3a23bfb415c4ab6261819d86d416a9879ec020210f0051325e445013b7945154d801de32a3948ccd4685742b1ea66e4bea920624ffd42c1d00a1ec1d4a56be6544f1391bb332241dcd572fab551753285df25a2ecb519562832232b4a021f8058576506105b5827722756713b4c7799ad18274d0206748d6c481fbbe34e1db5077a08bf659d77e3dc2171ea368423d2f351451f010b524b3fee28e1aa1878efba552645a02703e3f28f226d55b4317050095833b79e26a33609380e0e911e7441c5771cf31a32a172c3533c5198376415a43c3a02b06e83147e79b7420147ff56881c00f0562edae5bf7c5d62d24183a088061cbb7d1b2a43d50644443d2eff251e097814931af5bbcb40b9305112be69b265a8b32a2fe0afc209d6cfca59989e4b3040ff9b4ed03e7e4abaeba807a729952d027bc10d1e4ea7606861ff0623e9a154517258765fcf3733e4ab4f7b60fc916b0d5d2a2c3f6a1b3e4678725979578d7e8177af2a27380b7b8c429b5829762152bc848867f57b5d4e97723a10426f971458190a24dfeedc343f029e59d50aed38a8d4855a0092d354e0660034b5d94b477a54c125d4333d163e7d3c514cbd1b5fe6f5075463a1ae4d9194d3468c34225fdcdc5c38a4f70a252b6bc00bf71a7d15c0340e5d53aba6361dd1ef6a8f242013e229711406e5227baf3562495e55f070e6cbec28db314e3915858c5c21b284791c3228390a707069629c3d66bb2a0b37e72bcf5e583b245ecc39b966ecb07e6f8d9c100227e6e7651922f7415d52893158d9990c3717443b326b891f14a2e465be1011466619cf1d1cd25e0cf6846a33d2b57567d5e10e7a594caa046b291b5539c03f75b5d81464dc04c57acc761c0f60fcad4f88d3272ea123e21f8f0c3e67751a4a0df6ce5548a34b0f63c42f686ad7ccce1754a6c67b086e075823287a1483abe811500d402fb8f6c35e56cd8a277cd8a843eb4aea2ff23317509a2079162065512a9fc2d36a2b74950ef0517b123fdacc6c78b4447dff4e8c2b955d130ad763b477eef2597cb666415a126ae73292cfcf260c6c9c58eb1fe17053b8194072eaf61d71da1c29667006078a94ee70d56ad33ff1f93a7350113833cdb5eb482acd1134e2b18e0610445002df1d8958042b21271764b4799d16f9172f4a453671a4dc6bddeef703d9c75d4f4772c16fe61c5d43168def13cbd4ce2bf9649509f726063554d68922f736447b46d0995b6fd07b4abb8a823903784e2f216f920ce496e635ddecf563899f5a45fd3dcf208e8e5664b1ff0d06ec53171eb9c39b2939fae645fa4d556131edbf6bd0ccc627df8cb324a93ed24510d5cc105c598e0daf588d0ab085cb0b2377df4fc2c6820fe25986262d8e1452a7739b2a3bbe1c768290417ce4f9b0488e0f6e02b43ce86c801a7e5b04ebdc48892f9b743470521a4acde02d629ba0714a4e105e49803a2148a1aa12ea0ba659e91d7a573e9edd0f685bac5d42cc7c60a5b4170914f9721d0f034f2e3deda42b9b75be426aa955155666e8403f5ce4010e5dbf00b9dbad34d8f18271505a194cd3312538cdc6142ead31f6470957c364a6794c499b721712c4f2a30d409ce7049ada1e296228aa7ae91e8f31094a6e73e3362274319c1567fc36540b6d229137634c3b0b38199e6d29cfde419af7904fc31bd56ff3c9061249ffb80df2f8fb4b0d8157324334de542d051364b8005a70da22450467cf88486e63271454f9901985cf1a526dcdaf6f0ef2a745000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b70244c04d4968a9e1062e32fc07631c404c65f483315f312f7525120df95db3bb077847fcb73594d3af12328bb52ff96aa354ef09c73d8882266370eabd4c4eff360e127e96075c9caf37ae321f639f22eb483d6c087d66e9613a67e9fb13113abe4e2fef6a488331d35ab939c6418138226df2dcfd552fef6a488331d35ab939c6418138226df2dcfd552fef6a488331d35ab939c6418138226df2dcfd55d4221000de1fd535a0d36f2ed49fa95aa2970851b8b12e42262b9572877a964ac8cbf0647441dd7b977c0d2c42c9386444c1783af0532530d00f892e6fe21b61e977f0401958d744dd2cd23a64e354158fdda215cb48396e88ec69261a8f5656bc919f38a3dd846b741fab309b0dff5b15fd356276b43e6722209248377c0d10beb33a1d40e8ab6891cc7e7880490523e990b23e9ac16345ec12a57a4af7253bda2b7525bda52c163eb757583d23aa37ae77d819be52f7315cca3219747b651534a24a1b7b877d11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061eda1468361db23b4110a66ef95a17a73bb79436fe21b61e977f0401958d744dd2cd23a64e354158fdda215cb48396e88ec69261a8f5656bc919f38a3dd846b741fab309b0dff5b15fd356276b43e6722209248377c0d10beb33a1d40e8ab6891cc7e7880490523e990b23e9ac16345ec12a57a4af7253b0300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000002df9c4228acea600195e57699b163e371a978c440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ca8714dc779e96a0331f51b8fc4f519ce4b4e0ff7f2083c2b23e03444900857f968071c4e13ac4b2429417eb6a6df4ac9ea9223bf45e73dc9ffe4052a0a0c3e04a8067ea474786661b85756198ca85e04343c29ca8a4b076650597e017f2946ff0dd403fd3928198254fe7eb1e20740d66e8e32c76c131ffd3928198254fe7eb1e20740d66e8e32c76c131ffd3928198254fe7eb1e20740d66e8e32c76c131fa48a785db9c4cb4a71c1cc31c4dcc76c41686c6eaadd8028300ad96932bfee47ab69df6959aa765c1cddab5bb678cc2832f0bf47f5f55e23054d2c72804be4715805f259d4186609245a924704e0285ac0f98970ad4d762aebc17e72c74225623db0693d710fe350cd3b166d65b92c5a99120b4fea9a42765246151d3c2dd3359fefbc066d65334f59710d2a0000000000000000000000000000000000000000c64751513f6230192eaf01602687415e253a0d5b571b651d2e7ac71679cc2b3ac6d50959bb715a0f4dd3f33a3418350d000fbb161ecfde48802f5f32a2c59311ef1ff26244fcff5c25cef15ac4dd9b1e25e2823748fb2a6e703e96484bf1817d618de540cd924444ce5d964c1811b26cefb1060d7ccfc37bc842ab6fae14032d764ff173a17a771f2a2bf7476d255e48e2c96d373a9428105885f94d5fe1074365c6f622a9b05d3e8027d059b6325433c402715200000000000000000000000000000000000000005df81a460a4eac2110e0c030a39dff0f11a70e1b780f595f91c44119cb19dd6ee5425204b0a3236788959b2e2d6aa84ca95be3440b1c2a4679e510257714cd7081501600419b9341ba18777e8edc1f33a9dc49010bdd3e6055de4f66df50e759f9386867411f8c120c3451629fb14174d5f3ee42bedea3348d9ace3fe95ef820cb16c7423624223a1af0c00619f9082afe9a8337a8e00a58bf1c2b7e67cb1562b2196219a414ff360e145a721f958c534dd7ce700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a52db7c23360c4f572ff26e9e12874e50ab6f1edc9957264d04646f8210f0294880c71a00ba372982e8dd769d1f1875775aeb525cd5ef159ce5db44d756bb0e5df462676d2cbd66c161cb40a37d533fe00f603e15fa364ab6fd6e0090f888398f1b3f04b5456408b428fb6fb5d2540464518048a5f05e0c8f7d3d509e55d8663867787140f10077d6ef6315232c8b5d7f7aaa63bfb25c3783681068c3393155b0ea316f1fc7dd344e7d2b2738c96f547ef338083952db0d23360c4f572ff26e9e12874e50ab6f1e3172bf14f98bd85e0ebe526d2027626844f296071d26834d7223ad00318e1b34df698f71c962934167f40a696f20e514bfbef55aa8a9067a3139dc4607e73c7aa53603336f719e40bd0afa1d0e77d90985028b425cd3014534cf2f669fb7517374cc811d35e82854e73acd79dcfa9a3e1ed1cb57272add22e76d0878a5bf98268567e0138c400476895d8620d2bf181328c3372caf1501465984bd410e0b5816f1e51b07cba9a413af9710124ef4a47713b8c312ab1d960042aae62c9d7a7b2defd4055c3ce7aa3cf7cef0743947e4159d8bc324d3f82a0bdf9c1a5d9624b42cce5e5033fa1da236ea00e756e852445b27de8b32c747ea3dd1f00157ae0f5b21ccf05e33c29f9707d2fe5f4cc943a47c8b3d7d4f7265243642e6b356f3f8c110c6d79531281b3b5b5a0be91aebffa84be2bc380e93360a7535e6891f7aac9f3cd786fc3676024f7023ff3772e2889c627ff99d3ae1f97047e3bd0146c9d662234dbb7e556289b11b8df1b630e23c7339d20dc25200879e79f2913a43c426822b90c0ea77e30b0460fc8b5f4666064532cfb8aa251dabe927fd26036205e90c5faf565e6ffbd13d4435042313a5e19b4b8894197a8757b94aa5c35603563bf03d7545936104af9131b68ae26f806f3657c99db46e30645b022d4c99329d0d560de87893411e188a2d961387624630ff1885106d0b2e0c887e8566927ccf24bd6bd675130883eff63ad1f97057e3bd0146c9d662234dbb7e556289b11b34bc8413166e794c0aa6ad1c9f58d84661aa910aac380649bae2661e5c461b4e1475ec057f09dc0f9f94432f25980560e491d04286e76e24d4787848643c27771de4b2094ff83521d48c324ab6016f27cdc617180bbd5c0cec3e692a92fece1ecba5e03537330c59ea4f025e897b0e134600ab1fb9ef42036370856ee4ec364fad7d875671596d5feb552c48bd481f251d1af82738fbf44d00e7467302ff042bc1f97067e3bd0146c9d662234dbb7e556289b11b6488cf56a14c6968b2cb63287f32f3439d9e3164d9fb86156d0f7a57d9e5b576fd9974570f6f1b288834f36313e74e7b4f95e57911aa324cb831525ede1992435f36c0736c2474148ca0473bc6ad045631ac1917e3a00f1a1b97cd21daa3a35019418d2a839a127457cc5d2fdb981c1145666921fc4a6a1e1771385fb4b9d70873d9075fb06bf924dab9b50dc0d0694717bcc6640d66e0299dc0f63dffab99440300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000008df68f69b4f4732619e73a099ac22d59b8688c2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7120c5b63745b71b3c2373d88b0314704aabe0d5ef6a755e3b58048ca59665a01cb125e256a5823033bd45d9895044916cbb60ec561bf741f740b51d01f4447079fdf1dee8bb6796cf6147de6b3a2733608bb7e7de5cb22833da368191bcd477e7a6a690be58d72e382db50a9c3f459c24383564580c27b0be58d72e382db50a9c3f459c24383564580c27b0be58d72e382db50a9c3f459c24383564580c27b0300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d000000000000000000000000000000000300007d00000000000000000000000000000000867fca1efa30db7395083128126c5a774e1c234a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e7315738bd10e524af90f661b47de3f42ce513e8551823a773beb3825894806c23f1f354f2489135dc0126778e12069c104584bd23e5a44410f435e2ac72d59269f5e53eacbe22848da41745a37e52840d1253d5fef0e03b1fefd279ae31311f7f1a374341054413ef44b31d2ac132dd1b4a11f1e95bb6b341054413ef44b31d2ac132dd1b4a11f1e95bb6b341054413ef44b31d2ac132dd1b4a11f1e95bb6b1700c478fd7b9707d5e5451432b48850c1d22829e6acc7630b731c525d4e5d100796da33af293f7680c04d32e4cfb93ecfbabd08d98e0d607cfd485d441f167237cbf043702c4d4ec7922b28d167c370768e277da9f20b035a33175c03eec81084ffea7541f3a61201ed4e57386e790c07eb710140800c4c3333900c9fadc644046a2956586a6c217045136108218704aa908d10ce87665d7b3e816e33279220af637c46fe8c852c0624c57d5a1e21697885bf240cc3964c2b4eac0502b24c435e4d0021fb62283c058e06427313fa52d23f3f6e8c4af629b44d1f0569c14a474c144e080352f04d34a04e3d7103166ac78bc3304f4c581f3e11d2359b76fc4133521614d2caca5796d4704a1cdc083efef3df62997dd1484e249318717da2572be396412f316d7e003381054d059903a048fc4403d8a21e75bd876e16b340526ef0c33b3f8e88055f2c71452850333c9fa23e57667ff74adeb8c60b7189dc5863c8702f40fd3f3daead16364e8a673e8a5adb5a696d64677b79e85a17e2f32835b80218da8283249824f31bb014303d1542394f5aa1e9280c76127dcafb84350e7cc17964fc947eef63627355a8b52c47954468db90b20b646ef74a33249e2b3e801b7ba18c622fa1867738de116f36c1cfe51ef83df70015677c5a6142e825abd5d41ddba4e573c75736511a28d5024452f25836e26539fc8dd378ace82b20b25d582b4c921f607c2a29431ee606518681a559cd6cea1ace382527cb93344f463fb22b10db9802f6c071786ca9a64566229e0375783b4532905a470d34bf0199d1ed32bf91cf2054ee7a21b313923197b10b5e9ec1aa3a35f0e8761a129834fb1e6907f3e2aa60bc1a280a847c657bae51915e7814121b893d2247ccefbf42e9d76750b055cb1d4641e946a604ac73db87d3356c49fa6aba0a503e3c017b3d0ed024561f4f001fe6fe9424710e943ae5be0c26bf4c482d928dd760d7e62c2834e440662431c92b8ad8e811699de2690afb606a1915116d7330725b9c80da7019910e433851c51eb721a24ac6e11f4bbc54322a748144027f5ec45d3099cc5bd148f73d1c8492542fecd75375713e467e3e433b24230e742231ae239137ef7a62d57e67cd71d61090dae3384333212b3f741a6cfdcc117625623d58e8e8812dc8fb633f2a05ec1d802a0d16fe5cff58426c7f3b04595e68f17cb608f471a3361ac690325fc1512b303c833f5f85d56679095b23fc304b1dd4523756d6aea12d7bb36e224cc80e37afa5b43aab9c742ce00ad41aa7384e641de1e179935910413e54482c5182a86b4969286df08ee1287c8dc26f016a8c1094f3d45ab3421710154970757b71a2431c2a7a56d2a5741d97cd391f612dc77cc0612f61fa55756b676c1b5690ad5c101d43c427ff3f6061a83eea27a616b92a270c2331f59b487ef7f25f407c2fca51c82a7827424c6f000d566f44495d7b0ec766bd2f4017c76596b98d762cefbd6eaea7f03ced0a1e684427f07826d2bb658c9e8d37d6c99d40b766ae479ac52235ba79807d18a3005d9e2c830e83f68d3c90bbc82b5f8887147cd7d465a69159617fbfcb0b47b9d839c0e93625ba671646b1c26f6bd9787006f9e5b9598dc31a5eeca57671a2e8c12f1a2e9e753eba01467ec0b1687a3cfd5dc9102644859457525b578204cbb34e25eff92b53a874c0001c565361c57cef59a002c62e3048f1653edf3c2f0ba57072510a3d76b3bf9a168449841fa891171a4a5bca6194c826038f836544fff08d641369da6b940a777939b83e3f60d8df39991c013cbd6ff12707e831217c8ec8134f33e809709d6e77b196451c66443101d5f71c46da2e9524f92e4e4c4b122f34f621ba272362f507dd30a7213ceb14566c664e435462410841d4db2e7ef0056f1ae27c3bfcbe2e006f104975e69fe328e782dd74ac5adf1b88991c3ed1c1d46c420e941db21e25260263b16b37530d6d05a18956af7ba656b85c0c37c0e2995614a10e7611d2da211f5e975ef5dda568131e9d0714268a1d5a3c6e5a14fcec5eee63ed374a3c357001d90848b58073666f88822be98574119f51ba25858b397353a50177ceea04206346154fbfa28c01943a9e1585b06273110f9150b962f01c43af25518caed6404b985430bbe524486404f770436f793cb2ad620013f03764185e960f1c2f5805ae44de073c12386d19463d50223c14338d3381417665192c80cf9179c0cf6e372169672059511346cb4fd93f1b31222a3b698e6354956518776548615196ef5629f92922e43b5a214938c264367c1932cf3af57c2407342fc06f1f31df249403d536456f4bba7b26e186d258ee372730c5632e48b60c6f4c2facbe12a9c3943b56410c002799b93ec4ddd806e499f92cd22c6c570e9dff374b3d017a9140a62a2f16084ad88f943b28ba6f0d3a459b6f094e6a1d19431c46cc02104a35ee1f0544ba101588da4679aaee600c4844d46dcd144d634617a57a70cce730847b363fc4184530a760e92a824b6000d1dcdf3c5c468045edb61544199e4e407c0ba01d9f9fc474865c1725245ee20e0501a766312d4c70c7f7cb7e10e38d018e5e0621e05df95502d8e35254ecc54a0131361322dc5e3f8c4678628b67b41c427b8a62dc9d53377a1b1e639fb5d67b1d7a7605c9e19002d7dbd2195b554f221ea9b22307f95d4473c19d4781824157e68e8e0334e509582fd7f50a49f6ea1d5d75ab644d65d842a916d1565052b60b583d1d3a07db6647d21f7e092f807815bb157971958c000e7e9e2958dd36b904d470081c2059e826dae7da4abe74d03786c29049ae9dac3cc2ee796e3c8d1e693873e7534c46c61a92169a3785aac05ee3f1d14f37537528a4216c374536ea36b4ddc340f533bf41b81aec2f53be7c4837bb7f4e4805170cf3730f6542be7e166a163e02ccf2ca42d3493f75622aa42b16c0212d1a48b3502a0f577347ea9c60f3967861269d3e6abf9dcc03a9b8994ae443c16259db5c5eaba05964d67e3a3c744a2244e952dd20d0b5872d10f22b1c50d0c3579b83350603e0360a5d432a371803b95d2367446312b2fd274b21c16b7549637925aa51398bfe104d55b5686e3852ba3f9f4dc32f6961ad792ad34278da9d3020e814056cc762520c67056e2c2c4a1c44c5a22b4ecf3bd419322f117c556a0e09e8b66d58402f0133aa2ccb61bf39472e0eadc72f5d389649822913071e768a0b2ce07025cdda5075d47d0f3f3a688b7471ca466aacf30c63af770f2667a8ed685a69390e4f3dd643b9b75e1b91f18f4b2cd8195d2d5db1640a11df4cf265d06cce4c2d1fe55fb8537c34824d29ebd065063bc10fa079e12177d8d518d8ec5040c109230b7308d2141ae4127ebb7bc43e10ecaf5991239242df6ad00338a87458ae88ac2242e78e267a5dc7339490262001e52b7c0a3cbd663e5e3c1c5dafab1ba4021348d0ca75127a0b911a5d3d1258ced4b32804b26f580cf14f7123ca05732a15096caf3e1a1c2632ee371f8df87c4a5565095438ea2a76663a0c758f3e67b97c674062a1945c4dc541179d94be6763e2680418b18549a74b90747be3470f5fbebb22ab212760bcb24a0a99d234792e0612262378c04917021122881f984b762deb5df1859e796c8c577b9466c72d79a9c7154bddfd3d534f4612fb3c913746b45f78348d2745f566ef65b323e4540c5ee8228b30214d27f9c53cd8704d5ef104b035f5303e4c3f190045bb2ff75e704d161933221b732a1dac020441b740e6ab6b67fed2db6a4a4cb505cb26bd35bbcb947cbd76b914d99337418098ae48dab000619dda5a2a9d86241e409d654af8d4e70fad965e310c4c275ebb506966f734d92feb33ad2f38951d017931e06ec8712b441869471cc6428c06510c2e2bcb2f58528ea99734d3a79901b3d78c568a878678c22662003f1fd634355301524c8015786d85ad3f9048dc5e54e9fa3168ef7659c8039d1d7a5e6f3e4fda6051dcb7800bb01ed94f31f6aa14f1aca202c9bddd07168afe133c7ff6231c235663300d7d41015191136588647b46822d19e81c9457fde0612cfd680b347bb5af797a1fb85da2624c6ffa61c33b59ce5332cebe270ba444f06942e4881dab119f2adb83510e48368a1a4e16ea7259dc9965ee53bd642361f50711a2b80b9cf70020d5908a156368f219d017581d2cd5c860e447ae32d4ed7020e2a7122773942034690a9041d8ee5c0a8f275d25ef140c0c9c8170701987117623642b7cbbe32b46c6589c3015613b125cef0365fb47ed43b5ee9a37b17b0f260b34552f4460816b5091952a2a1caa7c8bac6601b4317e03ae41b02dade664401201441bc6debd4923213b44985e681014904d45d8672d34d91c3508ebf6c30bf94d5b4cabebd65caa07a46e9f23c363e7b00c72985940642b47f2779ec0f63efc30154aa21f7f6250204a117203780c1e4be9562b47a019d29ba7544d3f1465197f5a14b6b514371982d57a5eddbf72efd2674d08a405732550d47555291936c58a3b15f3c8385391ee2c7c2154f360a878e16c2ae98f7bf172764253775750e3a1ca0f5bac2d01f6050d33f172764253775750e3a1ca0f5bac2d01f6050d33f172764253775750e3a1ca0f5bac2d01f6050d3335ffd5357986bf3f3fe07b1b7b0f2d3939013e54a72e5e016dbe0e7b3fdb87282e9e123b15b4dc76c0a80c78cc6a5f2f6db7d83ddbb9845da39b7d14a537714c3232bf3f17b4912a170e6505cd62714a7ba8f871777c5b2a3e81382f0fa90e1477be2a25eafbec3dee8f7e388a96162564911c5672eb1e53ee51d1683e2c2a321c1d3e204a49b056981dae06fd342479fe056f4e6ff4281f476df91110d5d3080394024499ecb33bc4a324631a4a7b17baed860ee04b5e5fd2223f37e2ed6169d6e25014dc3828371fe17208f76d23231961cb14d28b601f20e2296be190205089569e6a4e60ea0c39f43065cc16703617be3e1988a4a21c017ef06a1b67cf01c8e6d3585a0bc72cd79522403905cb702c9a1047d6cfd840ebd7160ec7fa64217bd2db0ed31d7b13a8957352ab7d353c8516b24a376c9876d7a906767aa03d14827ac24049bec074d565e20ad5c0f829869a0437fbc4b02dae0342238c33845e1a059d2db5da2767ff4eb915a2aac6401a18990dd15b6f645f28f91de9a139270c17e236ae232d36f9d16f2b24b946035231ca27f7c9e22de1085c0a39c734258c900869059ed017b98720195404de67a15613658812a6198b1f0d6e017c133f2b8f52491335ff307aa73c17b556ce295dc722660b60690a43faba089226cc7361316b6ae3d74a182ac3a1782722ca59db82600d2467ba7821d5dd04dd771827a362453ecdcf4533807b2c697c4ec61bd894e021ed7ab0271c2e4e6cde7ebe17887cdd0c9fced660fbe142199c88985a80afe14836879d2b464cfc07ffaebd7dbe87342f4e24292a7bbee412715deb181bed7e0444e38a1a61d64e1d168e0d6fd3fb51456eab783e4454120d5573d468583c77060bb99442e8b6bd14c275d83069ac5423cb8c1c798d87241c52ab423047d75d7b1c83301ca7f225763a54d86a4d98af3d8a8804540babe12f141b2b1dce18fd078abcd8503db9225dfdbf524f5acf235457111b039324be14644009168a37d51d8ffc636528b7a40a7bd1443d0b407f0cec79830b5272bd0efecf0f646e638e61adf49d1c45c2fe0d98f4b52e3397b8528f0be14c8438267b1b784f796af5df48b21bea1f9986ff2211a66d32310f8b715d5d42197fd3db07043327426196ee2d630c8f5e7cbdb62117f34546371daa7d6f403f28fe3db315a9ff5804f7f2c038920e08755501ef1368511d7922b87331efb21f71be1bef71f2de7033b88acd03bc23ce642f023340b67ef91ce170ef5a38ee77105b0ee35aa519a674c83c1774dfd49f28cf5c02575f48f56ac58efa5dec29cf70e4db8a509fce443216b6136dc1f8e212aa4dd315cb0c6d1acc431d677e152f00591d811925504a67caf6742cdf4b822267eb136877af2513039f953864b8ca6df903f332ac18115ba241dd5cadde667b4c80217b5fe9d122970f0108a39f471df93ffc6fc009c82f6fa3f653c8576c4edf8f822f4f69843ee45e87605b746078ee40fa6756e62a6fb8537e0976a94e3ba54f8f623bfd1e78285da6655479731f7dfd142d05d3ac36e6ae3d1594561a4d42502a42252441523c67fb1d1989f3760d98bd2993c2a646c4a02819a19ca50f1a9ef94ea6115a4cea649306cc18b144df897f7792b534264bd65e0c2ade3d5cc677bd3e79d92239af2cb62994a22754ef0e284fd024d020e6fc1142cd94e720058679181ccf3051dd033b3bd6ca4434d9422458d3f978426efcd72accbc1467b8efdb191cf44f0d3c3d2d6582d151613c09351834034132b3800f11a23b6116a6a6284fce12300ca373a2493743863c4789a363f2a5454ad7f7fa1ffe09c13a9d820c405155184b2cdd532b5595353a4dce8f1852e0636e955c815eb698cb69c976bd33f3ffed45cee6af070dc0ac6f0d8a267a0477a361dedb163f7a84317700eae61ed6e52551ba53dc429c49e63a73e8f612d46a3226a70c8013162e2e2bfad54c655b88e5559f6e1e5d4e63062a6eee7b566f80fd39d5b61b777cf59c559a4ff54b14164a263f395d7693aa522a126835758b5ed44ba7e9947a91c10a592a80712a89af6e4a7dca0d208a929d70f1a4fc7810f2b04c34571752869e5e7a355a6722d78f9975916db60a21d42e338c08296574a2861dc8bb396d42d2957a8cb9d37126749c21345209719d9f096fc2d35100b0c1347c5b5bac61d96650179a9d8f34e2301d3470c2244de882213b45d44e2f7dd34a10769b474a59c50c767041297e0a574d22b347dd4a32986404a6a5f976b4e1832c2e1af920ae47ab5db8248f453f2838073495db53e1d43579b2168907286e3b1a1443c01ec852832ba2483c77c0d3c8213cb7a304dd00856d60fc716b5d3d8f7e1a495a0df5b55605f9815f5ced996924fae0c30a2772986e233e8e43753ad16005c977197bd0ba3e5c286d1bb011e74039eb765b92e2ca3198ad131b77981516e3d4ba6db28b8517078ec507c3abd01dbdc9a045feacd20a68829e06c2ad710a9ab80a76979b684800507b77d35a371f7f12636a8ed8c25e7539252d8ff88f655b2db81346605b68796e975065c72a61c4be5f6d4717e7755820613e06c22f0f6c469b24fc6d2d26c51060096bc64800d987001933a8037b243d6421d93f91021331eb3b8950541cff50b92b09007e79aae01f6fad796b573f88cc007bfe6300f5cbfb2f4b7a3f5334e1105d46bc3e3f402ad5245966213b955434697691c031814d135e6f83091d92597d0674c43465b7d70c0b2342d13af4b962060171d931a9ade372f3e2da7c7828446eab8d2436291fb2632b20526433699163412cb6176521b17ea8c52737db02ab726839de254b37694506cd52140d4ca319c6d2712bd82c82793bf76f14bba75678abf68a5a03026a55df247e16fcb3ed0629e52337848f1e7566955a63a557da63fe22f23962f66d2f2cd094174aacb853a8ca170acf3e3a4b2738564a225f567e76b1c27848db7f07ad2508749076fb664782d139eb5b9f3dfef5e77996af4b0612520907281d0220d6e916646b6cb910e44fa91359675106e9a47b51cbd9c11cbe539029bf86e22b019f7a5205a81409515abb42aee7fb409328cd2baf27494ef9efda205c74455292b9641379fa6255262e7b3f2119211d4bc0af2d7e1f863e2dc0517a9485bb013353ea5104b91276864a2b231f21fd4ed2824c15248fba77004d1e75eab46e42124d0457bcd675513c0609654c898b578e6af757ae50e3207393f248f72f9d293338a0400edd2f0b324afe7db1d9d247eccd8f7b76702e7459ff1044152b565347a0b263736af471b3ed8154c7e3997c6db0651a09c27835d320e66b824a8d0bc6b15d0bb5f7040ec6d2ac6ba8440f47c730b765c7d288235318b65e5e3fa916141e06269069ab68783d356ed0c6ab35cdf863382e3ca1200cd03b0fc751d7002302b04d00000000000000000000000000000000000000008d626434b6cf062446ab02040674496e1f63a25cfeffff0100000000000000000000000000000000feffff01000000000000000000000000000000009362e720b87f7f0531221f50c2129718141b704fca28464c5cfb6c4460b42a4053409771e53126553445785cc30dfc0d6710112a83c69c5789046c40feffff0100000000000000000000000000000000feffff01000000000000000000000000000000002fc9a842cdbc0a3a9e783f3d9d07e65b55031237d027681b60a0320b4fb8a116f6c9823f3e82777b8d626434b6cf062446ab02040674496e1f63a25cbfd85814bb42f03407105560160bd24240a10d2dbfd85814bb42f03407105560160bd24240a10d2dbfd85814bb42f03407105560160bd24240a10d2db1ba344d7a1ba7747ca7eb7472652c5838389452e6978b427861e96848ca8a13e79f5e67b2684842f7a1d42b05fee65f41e0f63ab57341397d765a4f3aaab566ef0f356a13544c6cd2a4076a005c430ea1329323af041e78db3de50120a3ad5718cf2a7885cc5f268eab6a45068f886e92c32c2e3977af74818cd52e2ebde153575fb11b6595ae2b167a0175818cd52e2ebde153575fb11b6595ae2b167a0175a6ee781bcc6ed53c7dd568246123665b2ba19705bac658498277651ec05bab177b6dc84eb6dbaf7b66eb4d2b100f856a8d5ae22edc12983978fc294c40744851778c002cf6f4263f2454082d73856c1bf4000c69d476146399193433914ef213dc6aa71842952e083598c260bb71b222835bc62ea38206606477887b75d8342b92fe1128442b09589ff13e740e080c084abd2604e7807b71e30a2531e7a6b47235066534467d770e336c48342320527665baa871bcc61b211dd19b2bfdfb713a2f38ca603b913179762e22672630bd4112c05f78de8e350dd399ee276587574f0d88565ada3b5011c881d348b2525264174adb00be7fba41d2adfa47afdbd2397da83d5f91dbc23b80785b214ad1f461be96583b3648fd4f5c96586d38f332547d09512eee6ab96b4a912311e538ad243511f16da61f9b7b79b1b43bc929ea27622a416be79d407e49f9e868cb6aa72d26ce370c2b4b2c76c6e2683dfbf5031ac9bd7c493f16d46ad863b10b0a187c6cdcdd782c857df1196b60ed7a6843416dcd5ab350ced21b3984b5932984e0e92194f9e425cc8d5558dc30335c028be3423e670f6d96b573413024d553316bf701e0fd0e6c104c661e71f8314cccf32a5bb94463460840a82501813d1dea99f859a9011c5003428d6087b8bc6dd7538162dce0e96a0db36300a714343ba6f012316b14e429a218854bb496552ac4096e63bfec4727445cde672acf8876e19d0054be2335379821966faba27a574ae47461fed4e434cbb7752f38d04439af6ff57a4dc6da569eff25111076555b9c1b0f14224c68098adecd438d8a471ea1460d1f776a4a0aa1fef24bd06aa1522b227022e0fdce3f47b0e95e879f0b3b1e06595624e7b13ffb5bd9300152dd46f94d305ca74f7d1b83b5493a822a7457b82a81251862672b94a3c72d6025b86e9c27720d9376c00fae6f4069350f68499fcab759776e6e09e5db277b0d914849feffff0100000000000000000000000000000000feffff01000000000000000000000000000000004d05663abc5f1d6c164b610dd0ddd42b01134d5947f3801fe5a97c266fe7e61741be7d41c710891d74f2ab36405bb5726e72b16a1160ad52a2b5e251350f68499fcab759776e6e09e5db277b0d9148497087592e0d9fa71d7154ed052622b634009b340e7087592e0d9fa71d7154ed052622b634009b340e7087592e0d9fa71d7154ed052622b634009b340e2e0a331ff4b16e60fd6a8f6b7e58da3a7d9cd908dac8997b51edef0dd9719c3e8d498751de151b328525c9700e55c5613c00d5568e615e07d64f6c408adc1e0218182c6e7d2f8d394f876f2d88847c46de59140f85c357439708e445f9833a7379810a22ca999b5f6504615105c70a0ba9c53a6080c93b0a112de10bf08b707499776e41d34d93640a052f0a35b53c2774fbbe346da3d1509e327e393d02455e54a87e3b8c235c4287670c78cb5fb832f8429249660cfd50fee40217427d9a1091fe6e3c527b4b3eedad2d655aedd33f614d737173c81069c7874b0509179f328afd9b669023516603e5081afe4b7c380444202e62aa8223931802258adb5106f789ce3e940f73088215a76a8a752f0bae2f9a6915061a08a8aea6141b85153393aa35773a44147e7b8099722101ff4d534cf43edf99b6199d75f64c6264840d9827bc3e10871e0395762a4fd065085829723c6abe9af160f5c0b84159927c348fe6154b6cfae27e469d6e0a3384d77627d9146417c8c6445d97c944a5033e231f4ff62e044563166e4cec283b1ea600a159e215732e590abdfe5f52057d6142582d4d7602f3a60cb3350e1160452f3ad0365d5fb022947d18bbd33fc51c1b410851075fe24e2316dcaaf313017f121ed20eb809b7c2103513408432f9a7d730d951c50090903712a76d5a6a45dfa2653c587a5ebedf5e472be7a47c9f77555119ca9419db83d45ce743af1730b2ba701344ba686ec1355da0afa95a12686227db2ca46797df580c029b687ae22d305fe1ce503b70630507fe030d0faaffd16e80d425393adbb0166977556dcb43d12efb04d3722c178c02a4fda420c3b22a7edf80e247a47c6355ad38561e389da75dba54fe46ab9c2d59c7a2264b10ebdb26f4402a47bb928806d1fc542523205c3a1212d12c6eae276d6b98d8029cecb016339ae134f11c6e32177bed509600e5723ed1eb2c26a27b32f33c5e2eebbc861a8262592a8d70bc0ec2d5a646a1d8a84b2d32ff7c491c2a36902f1b0e903a2010c34c67637ecf992c029cbc385107f54be54af13b1bbc617a57448b1017f47978bf4ff063327e56729c434a15af91cd644e5914686e622f5052439e209343ef7b710ed969000137736cbf1223267ddb3cfec6710ae07de9346d44aa2eef4a85463e8d1c586b15c53b6da8b465d6cd9d3aca17393e5238d3397d3df434f33f1223558dce3cef6a2b1d2c5f8d142cc6d575b2fc88310b65a01c169c1a5475e87d684b1d841005e5991f90febe774285246a4aff433efff42b14911dc82f5e7139376a07ea395bfff809daff687c3f871b5835aa95754ab7cd66e205f564d042af388bcf2c7391b0b337ce3e592aeb59a507e16f4a2a1e577817ca7c7e577560c2469985fa0625a6062e568806541bb73d28f726c456aece142d1d140519a4cc435789ed62708fc60e6741efb05b95b9797ab6424d08b893433a99e0000c7e4e5b4cc4ff7a69b495281d86db4c5debf9b02cdb10be5f677bcd54c205670dd6c79b0733be5f5849ad822702a6ad06272d4343095b3b657ae1c55c8cc2115018077a65177e573d3b68982cbcfa162cb2c9e36167528d083fd357287a4173335ba5e125a4e1651ad0f6b937850ead01695da33542e6826436772628de07e02b2579492968ee9a7a2daeb9475a196f0f43e97b4d9a5cda393684a160d43631733c67c903a6e94a2901a2b72ad868a92ad2934f28a9ab6b5c594f092f1943971bb14d9c162aa20a7a9a33c11d80668a5ce3c40e4d0cf748180d54570139d25f6fd657db3890b3f65f5a34ab08599c0d5748bd3d1935462c08f09bb22f9357c842c62643339fcfcc57911aff43200de6582d0c46361666d76ed2bc431e998fcf36d1686a03b3a07255146e251b0d75861d17f2bf467f956e6e9f05b81cee76254aa9bcf6726e5aaf58ea79082fe0636d69cd00b5741db9ad698edcc064f26341352f00b02bbbf9b40abd37ae6bb067a84c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f51b983245eb971bf700c3645a8ad8586c6ecb4768a5b917b8b224556d3f7314c7c53b776f50ab1657835157a7dd7a3b7cbeac1166359f2cd26ec439067f7f64b75b825f76a7487c2f1a5b7de37a1b63ced4fe136859ce737eaaed569f440d423a4ccd78399aae1ae11fb45cff35114cbed49462efe8dc4a8107362e17a49a23a813dd01cdcfb926ffc5d214a5557e2a3d527120f4d37d3e702d47171b390e1be374262f7ad88f33449cb20133f37a2bbdf7562b0bb66327934e7308e9d2155c7411f71c4fc8905fb9fcd3449013c032cc955f0364ca814b108c74620000000000000000000000000000000000000000669ca64d5b9ade64bf794639292e9c7a6e358727777db01a58de4f51f2f2ae682722591657e083647bd4c075e1ad6501a2910371c6a03d31da0c8e634c3bc965da34bc283686f86d50dff550072a0f3e8a7bb71a4bba9656cb1a1827a933c2295de3593a57a24403d93f50347958f203fe35274924715f2934ad82222bcadc5dad34541ea29198211848d34805579d131f8bd258dad5cc4a3957c14b01a52c311a755348704c124b367c986579059a5bbbcf3572f485ff0c5c9b5657d0cfb7054be80d77d5b90a300ee308447e9f95567248315ff0584c724478ff080000000000000000000000000000000000000000c484a123f0ee8e4cbc9a7a10acb26d5b348b3c3d0d76b90b87858031e1b4a849c1cb836736f29d6e0dcaf5148e587a33efc39c02b950a91ec2a0697b0beacc3eaad67675e135e917661c7f026841044d0bff800326f974680b63150ab8fb734bbd015f669c1ea610de6863600b758b70a1bb000483538e31fe9c311782d51a3fc297de781272935ba389641cbd13d711eac28b4f44ead93c8fdbe96dd600ec00d70d5015b48d1033e7e7bf06799d8a1878d6973dee60fd37530c3f207454510eb5ba742c5eed733042561327b8e1c658e0df077e65cec8702464bf24000000000000000000000000000000000000000001533b56c18268277ec28d40380be725dd569a6a79b8254ac37a80636303131615c58e31388fad714a788d7dd7ba1423e353ff7d859f93261b52413efb2d4520074c6b4ec5ec3330c797e12eb0e5a2189836052023457d2962a2773db7d4061a2d74544773cb5c0f487d4c0ea547711d21d9760a7a532f4c1ffae94845af1930264a3f6647f3cd2f622e7519f59adf6864d7af7722501935f552dc02bc7dee2d504a102022accc29a0a8bc33b679672259510e4c69102f6f46c60715a4eca9314198a4241547fe1227a679149c13c1443e4ac266421aa03eefa8a8390000000000000000000000000000000000000000d88df318323def5d875200184660e8443101c43c4eaddc43efff8e56850b7a4aab12e60a153e0d23d8bb2c0104d48a4eed16350568ad4239aea1904eed43d972f3d4335186fec615d6ffef7d5550770e2d27667e474291037ef9a12b025ae67289f97b4fcd5f730a61feeb032444310bd01d515764616916d9684f65c0fee6710fedcc353f70b0652ee57e0d77157739094e865a12d6d122b303b4620d7cc62abc95f206b5823e2c53af4a71fde103526a681523e8cbbe23c6bd4b07a1a3407dfd917b2e2fc1952353beb225c5d61e4a0ab02a04691cf3134a7e120f0000000000000000000000000000000000000000d471d52640eebe47d081f97e59e9ce30cabe010c40bbc567ffb0905a45018539ba9bef513b67236c0000000000000000000000000000000000000000feffff0100000000000000000000000000000000442d9b5e3e9f4f2314837e391285be0305262578e1bb46183a1147658c914a39d7cf350d541c335921d3c82667219c02da8fee700a630b633ed9a25f83226346ba94a752675c975e958a7a5fdde7e47b85df640e633a3a03615a092e4e6c3a2e0efeaa189a3b8f7445e4d67a7308057a5caf03668be68706052406113577ec516ecbcc035ef24959d14fa94baf0e652e487928308405d21120b4ac1e4e639b63000000000000000000000000000000000000000093cd3c0ac90b091d51cb4d0d9e9d8147c15a9b60de7856582ec96867ed6a8a2128255638220adc6adea17726759fb17765606f7b79ffd8633d9cc831ad671a3edebdef07bf78cd0b945fe327e3ebc5048e474f563ba43d0257d9b97cc04bce0b2d7a1d179052816c7c3d6f39e3d7163f8dcf340053c7812b75fcea7e06e8833559b86f3f1970a351643e031f2634654e3160fb4cfa3cc206b1c1e657c7f9ab7b000000000000000000000000000000000000000012742d345917ae61965743362ddb0a1c077056222ec2031a8ce56662ac2cbb43f8ea3f4974399a756fd469057d9cb94390ae925a2aaa1a405507c045c6fc7b4cbfc3ee5543185f25f1ad8d1a72b0585aaadd9b521bc5e34c51fc3f023306d53b6017ce0ae9a79c22359660000289a81aa47d9c10a593d94553404f6589dddb48e01e64395b247a64953a241680063a0a115ffa2dc00a400b2b282c2908e8864d8a292f30cf711a4e56387872366dfb18243f2b0fb379bf5d97d8fa6c9229ab35efaf843c22e00d02a812eb69e6a263052e9f144fc04849595083ef2fe24c147105a9dc195ea6c03bc2df3d592b886c6afbbc156e4408a330cffe155763268e0d6764c703958ebb362f43a936885ad8181227ea2d6e53a962dc27c2083c9fe91b5b821f728014a04b1a336c0073be8849fd4ba258c1c4e1478418c30dfa241c3e8c5aac382ea45153bf3b1149980a4b780a8587396cad5b047612472456a9cc17cb192528b57b655fd542911e3e04e009c424886c2d00b509a3d09c1cebf54d6567530f5fb5b7735f2496a14be88dfd4359d914285401555711e5fd5a1c9d7821b06c036edc72bf395a54a454968dfb5df987cb66d0d1516d4abdb65f0e792d648c61223b82cf4b5f261afb56ff09312cfbd56f029514815df20c5200c452ce6aa5a53e6c50c7e4315df761255500767ba5b54637238690092003b11c60ee827c90eb2d37513b4c7506ed10181606421031d85743d9f84d6d47685b3ad16d53026ce05d6be3badf1804b4e26840abf71a53851c4e7cdebd3aa3300f00b356947d58ce3f168f4b115eb4fa0a5c25cd001198f8420e70ca7e77f6627a220a65005d9697bc4d0cc7ee15d2ad4504d648e82d42cbd721486e0c7d6cbeb1421fa7823bf998855aca78eb663857ef67579df50d52188c0311fee0539f28d11d83eca724501ba37b505ba03f2c59d47828a92a242a1ec32705cb18235e4b1f6c72402e29ac3a307bb1aae73d74952011668fa838f0dddd56a4b7162cf1458626bca0d609ad508a3e1af3c4796f78f7251835f1084dc29767a55b5624085bd218ddc493279e27af7a642fdc3f9eaade79a1e93f608d0034238c71c12a50f6010f6ed9b22bf3c7be1f7f305e249835b54934741a3deaedae23a2244426a522ea687846a92848c87c02b082ba538a8d440db60bf16296ab9079939d491b7a97242e81f12a3d15eae2322341142666a6eb74488a25490c446024ba02157172d29125d420b172df889124020c380c2c07014834c3263dedd5633738879c496f39dd397c04e543fae259425a9c7b62ac861100c91e133f5416526e35345c3069e3b00bd38e7f595c45b0509f9e314e6cd07b0cf9a6cb3614332768aaa7e21b4cbe2a6365267f41644f7a5cd1f7e72dbc88a332242cb64aba10706e0b6bb2291bf03804b27eb034528f587b9a30506d1da8ab185cdb3865cf6523315fab6f6e1b42c60df811405f0ef1400fbd55676f1d079279e3038a2c99cff76a9057b75a6b93c91b3333f735ab5b6c121238da7155dfc2328a54560fbd488d12ac60e04ac66e925d731a5124de3fc44a5545652c1d709069480f004865f8382163072c69b022d63912493c365b05ee441a8db22f64a2220260c27a104d075d6dbb6ca42720d89e2ced777d484b380e3e6e04f156c33e661bd4409e7477f9aa5529d88c3eedebe5233377796608e613728d68c079d7fa5f719c6728133154412c6ccd6f70a3abf46a552a1d0ac5a1e9703b6b6e4f82b16e3afc18a013038b61549a06c1531e5e8b12e9e88f5104a7776ebcecf9353c921a12fd5f39633cf7c545746b6a112e8dd44434a4b14ae3d3822b70449317270fb71c1ab2dc5aa626f8535ad3712278a6572a75bce7072f54ec3ddc9c905866b21668c562375cceb1290f99384627a4cdbe2f5052d94c19aca95750f2b1697e0bda50c5572166071c1b47c4c19535d9c588489665ef25dbf08f217497cc3874a0a76913cce6685df600647d09ca73b751ef0dca4b4a72a1bb082309d92e667dd4a65bba318a668581db1736804b23005c314faea7202870fd4c0e8b1346532f81ad0cf89fc60e2b9915134f02eb724e69c0777ec745170b70c5141fb30668d3b3484e6d1b341100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000674cb7746e112c114169e677cf39b450f4232e29545e1f5cd9f814633f2a694e63f5761d37c465083e68877b0b26445b4184792247b40803316d9c0cf64bab051cf9ba3323106d592c462c1cdff00e04beb9915d56a8337b67dd6022f935ad617c17c852358ca3755e3f953af188084b61b22451dad65701358ca3755e3f953af188084b61b22451dad65701358ca3755e3f953af188084b61b22451dad657017582164aa48ca03b972a712f0068456fd39b7e6d52c2e9269145e365a61a57713228436ce784aa2b00f3176af4be9775825d8e5c4683b633812ff968cf1e443160f2f53c8dc32d4afb018c70dda87c7e573e3b7b108ddc20e3e97f00482765403d21f6252a26215a20ddf7078900c425f54c6149db99534b8494707214e7d675c4124a24c16e5058a14d821b0c462837633935227b6f301a9b27464c5957515ce43fac6f904c0d7cdbe18477bd5c0b5de7fafa53ea69526c92c8252f6afd941bfb09900bd33dd9290ae049518af3a149705cfe39ae3c1f710eb7b3153aa6e445e1acc57389fc59694775550cf66849210e748116fe7aa309944b6f368ec1f37935cfdf09d883d102c126d20b6379140ef8f23d6dd5b4da7ca57f2864296a292e04c8f702bbc3d14c8423cd1d8e717168482fda13af5a8b014d5fa92c76f03a39a0868b47bef8be1143e36a2e719ed83e55d6893e0b86730baacba7739bf8a9640828f37e96f5b94a23761945a4ed25259f8a33622dcbca6a41ead623d38afe39862cf86b1283c02df6ddaf6aef670e21182cf9469d0e6b5c51a0bb31ff02593275d8c74beeca92180c74d833b9b0d73736bdc265ce14d34bc814b5427556c43d274cd62f6535596de67b447b23ba234791f0370bf9f00040b660397b35b451132c51197a211919600d297f7e3b95b44c9f02054f27f8966228932d693e33ed775676f62593f5643a0000000000000000000000000000000000000000feffff01000000000000000000000000000000008249b02d48ac464a4a5c6c77910bab7231f8690adad27a064a750f51aaa4657364b54f5d240d92077b7067330202a5194615935482ed9c63fc5db618ce7a77003a328b6c0d9a7665401fee7966cc356a1390494fa574862b5e78b024b86f67585051f56c604642197237ab0c8620d26d290b6f1aec234700976e67183aa7d971dcd9761a5ea1e3217eb9ab3eb0033a2d3c8fcf0790d2dc1cf6d78b4d8fd0c4490000000000000000000000000000000000000000dfa60226bd145b6ee4d84c24a9268631999ad52783ca5402711c1556c561d8043ddbd92dc5d3e46d23271140ba55976e4913fe366120fe51a380c10d0dcccd3f17a4f41a07946a21693adc77b848ea2bd1380d096549ea6cd739823aa06d7d6cd9bbf96800ab5241952d2f597cee020b0a537a08d72c29147b3aed405ec5377798697744317c620a79526450f91d57601ab0f604a306c81aadf6302a6e8e3c720000000000000000000000000000000000000000c7050b0b4e390f29ff4ea90347bf9618a3257a785490ac23f4ca504e98676a5b226bc25d56e58f35102414032e26402523504e1b1535d2535460276fbfade02fba3c487b1f3ffd14354c53205edbcb5ffd950d3a1ae0b65d5c8df725cc98c250893edd7dd658e557c3b43a44a87ecc0ad9278767a855570b85a161162f4d13231a84bf60a875ed731974e00a0aa5de335a3d29273a5eaa3ca68e6503befb0e1b4651572b86b3d53145c9024380699965872c9063caf1fa4353a444405cdb394b2183542541a9884594f78b0c1924827bb5adc73c2151d875469f1057625f053bd4826b57dd9aa4630e5cc060b766e72d28255567872d504cf53ad420bfcb381ee66f6b37bbe9a330eba5e45ed9d6106ee427664b7fbead37c3eff660fbd0854336dab113407df71cce64800e5513452310447d61936c8a1b574a93578780c65b1c7018184279d749698f26462eaf635180f8a8283914be5c20f3222c2987b454f38412438aa93e088fca496648db1f2248b2093c37fcfa16934b7625ce3cc919314fcc5176102b5fc067bb4908cd26040808380b30e7862a92173c4a1dd987020d48b36edf691d636b6a2e2c3a7cf770d705313be6bea556ba624d0d3725054a1bcaa756b972bc636822d84e36c723386b445e19d837884fab237c4b1dc16416f25bce332d5eb55fa76894096345850cb1694d5b13d3344a2bb5a54ef695f30ae565855b4356877e7626c1495be1810c85521710044c5719482fd40ed49f396f78d87f0c58f6f54acd6477410a6ad53e992df46170c6bc70694e260e5a617a0081df005cc14fd440683eba04b18ca44645d258096b95a7661fc0ea2d4ed30858362c0275e2b1b43da2202020efd65a4dc00d57616078a166fabace21100f792948dd957e190e7e17e25f283a529eda08cbffda6e3d30cd59e4e907024ca78e4198e8a32b136e8915a223af497b6fe05465335a2997869141c955cc316d31971525fb9c536df67530828d3434d48fe5136b8cd91942670653ddce8d44e6093e1258502f10c76c3c4a21e34911f94e8f419193053d18123565a353077d7eb4fa71b75cf234fb807875cfe7df0ba7157a7d4506266189e6d70ffb50756cfabd897bdc26880ff2454770e7e51a3ce95b1047537f1271b61cdf28eef3404c4b5cad51c1e0ad423f1bad747c88a3522427da5cab01b61b24c66d4d05aa2371b19241452558830789a5b33beeedd056d16b622295cfee7daebb026d1ed6fb52fcc7b4662c448549a6803d0e6440a6111056bb40e05acd76a3191b65b7091471ae35cf324416d873ee852959e99f523e023e9c197afa5e16cc425d7842c0677b399d2a4245b500337742e56a938a790c37992d6082148923c36ed6241e872806c7ba5b5e2d474379866a78484621dc22691d704ba5073a6210ca0b65487c6f2e0d270e0074c0941a31803f4376517e3ff691fc75ff5ecf5466181c7d1596d740b8678d1c80bfc30331b13412d757ac7d59dd1b2337571f631dabb2723b5b5f7c5a3c0403449eec3f6d0e9f0aa401e5039d89c94e24d6932135e24d6324f6c575bf10d96db90db6794f325c63bcffed6caf85a63506483b5428c4ad51bba2a4716904012c16fa11224e20625a5dec880bb4a4427ae133333f75100d616965dd65cd9a1b1545932a0e35ac4662d861c76ba95bae29c0117b7e6ebe6d6670469d58ec4432443052a615fe4e304405fdb43d2a8afc4d89def236c7d7c711d9f9503bb373757110b1c73925b2da207f48c83d5693942633f3ad0da970c503fdac061a5acf800243657552db501346972b5f49ba3f044ff8b92a0a3cb2a6794dd7f577be5e2046ed3d575052c8b35d0b560300a54ecc29f827f117535cfc56282788799ca010341924125c004f8001737629449c925c4646d2764994aba33f43663416a8b73f25927721745fc91d1d6ad7a21b794ac5776d95a1351aa9410e6cdaca745ac7bc385ca69c531d329122e4a8bc24ea62d00896d4e31bb875066d06f30d445be92b4417a87e6bcb54534101c7aa6c83838060fc19a32fa7128d05164fbf61979b310792e5941cf14610187e00e2342d796a1351402e15f09dc5301a84290a952a78048103f36cfb69946c4de60119802193450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065920b3d46800867c827e50fcc5df44505672f4c9f0ce077e310ba51dec8576b78226b5f33b0d47cf27eba1a7a9bae7b0108354274e6877b575b357caa2d7613eda06e331474c164f63d880f36af90303b6512363c36b03a2afcbb5b72c75f3eda599f596579467a5cb14c033e11b71fe58c7f14e773ab3fd838f20476620e46bd72c5668123e80bcf613465a5c2247c1915df28b420f6271eee575dcea85702e02b627ca8779745548e8646a4286252ce5e892ff5ec5803aeb0ca041f91a42afe1f6a474761533a7eabab667b61aa3cf85b5d312d84377baa896a514bc8a11f87ac325ebaf9f16871f5247678d3704b22c6271d2d03cc45843e552b71116b14ecc291791dcf5774e4917002bc2f0711b0b7ec483ff484612b2fad1aaca0ea482aff7301f80e692eb6ec2e76880e6502cdc5bd7bb3f2c90fe22e8341d8f39f7030bf1b60d682351ab607765f6bbc8456d05148134167191595847b0576d07b2cabd36848da47110e5697be5ebfe61b74362c9c7eaf4ec907c980c263ae42c2681de5da47ae1b9c2a2ec1be0facfe3a734ee3da2e8c631d53d11aa8601f616e159b8d942922b95718d7cdac43842c295e83affe69cd25662c59460810f392e22c5e0e354929f2b463126e5c7aeddad128a0a9e004eb1ac52f40db8528e6dc8b3aaba05375ceba2513644f924637f8cc619ba2b949ad2dc5638b502263b9cd220b5da7e55d5115c872ad82e46825cc5f5f06eb326ff2062c29ebf96b7db0db102cf29a576c63dba3280d920060247a385e76b8a8239f98f84f9174f05e6a34ff519f628e4d673819147b3959197097b933ac0191753846ea680b3da83d7592000994475d7e12f3047225ef984512f5b55c42268f1a61df040c2bebaf7a5ca88a6b06080c35055df6110e42ec328e914e643235534243cdd011f57ae512019f2550d503501674d9eb2ef9026c35c3ee6309fb1df34a28501c27f104e954c1650f21801b8d089009f252f49f7104fa7b9c5670f260550cecf2710b96f44a3bdbdb23824c934ef91205793923974a347e07440f5f545b4226a42b84c8780ab390050a286b907cce4a06117c0d4e732f4103595dc4e34767fccd7a47b36a1b096ccd67020d6b768737ab1a3cf684655ce0a77162d770635f14663950ed6e06b80a9d279530a352decf1a166c8355773b81bd31df97a126a0e1990f985a0f4cfeed6a2a3786de6b17e0ad4af8b215642108e0322f100578ca3332159b6ab52299e623758b7f1e6f53580178e343410f50b5f734b9f8f107e5136f16bad21960291f0060b28e0d00a9dfa514505ee5121b436220fa25495ba7c0dc26bd805802e83ca41f6c402d64429f914d33d5ea393fb4994b57814a7cebb6e93dda7f7473068f184e61f9c30dca27947338053f3730bfd6309a11e25257bf7944ccf091277d552b066e803c437fedda12c0c2660a9077e73a034c3e28520818347266526987139523dfe6dc09b7e2f9634a7e7f092b8d1306e80d454ddfe7505ba0d0493d8214de054f6af83bdee8af6d4a738a4002e3bc5eadfef407b828d9122b47fa24af490162b9dfc24916f0334f34e1972311c9c115c52a146ef654b238f59e0124510f301c120d6810311651446c86d86c0cf9dd6c4dd33848be0bc37d1e85be0155f9644b88b050452e3ae67b07104a2709c11b20ef37707263bad43415222a73c57f1256fb012864a5fe084aa6bf684f856bed435d436f10cf700b39279cbe2af612ca73ed101b22bd56020ce2e12059ab3b6c25db0a6c491fdbf61087fab13dfc7e6c320d2d8c1d15ea4121676b491e2c47796237d62838a215dd1cc685bf3c5ece201cbecf0172beb27a7eb52be3661ce7842b7c725c436f9ecc49939fc5169eef900f5bc9c54f0dac3b6689b4372c184d4a53eeffb920019dc94e6d9ed741e0eb8e325261920068f7ae7e5aeebf0c40885770daaf783d40004d4fecb30d5faafd5f580ca6e7774c92fd079e312c3484848c3c889ec55b9c5f47580b61930bb96ca31be394a52102204070faab3822910be714c814b3295997c87e1ba56c255fb11a468b3537324d019958bbda27423626ab2c3f220568dbb9645bae488c4ea48ad65c5e0dcb312b48ac6dfced65082cda673575850a16c0e85c5d1f73f64ab297d25597a7182d9ef3d25a8a63353dd72c9b51004a03207f64f65ccef74908559b2214366d2378dcdaf8236d19b062aa8b205631540a3ccb437f5a0ee86e50556af113968a0c2f65e09e14f87cc876113f5d527d0ebb37dfc84115ac6a7f27320da8375987b022a00e565e6eb73f2b541dc356264b976d68d721394b698a3449601c4f75d6e875dfe881429db9d43797395425c1df953389290663888d0a02bf65045b86ca1d7dbd3b9a23fbcfbf02e5a4283bea578b65aaf1bd4640d34d233e90b113500e3879226f30394855ef43e6d44909bdaec962b655580f18c633148ae73677640b311045e2760765e1d05478420c0a183f5c4cf243e85d51965957084a3c10b5dcda29b71cbb66b3fb883544cb8879b04020036502ea630cba013e5f81c04ff10cd33097c36a67c59e77315e631a7ef2d12f6514db606b0ecdf42bad678e29781df63c0a648c1dfba7a7611c1b8d3630091c6c54e9de318d079621bbfd5b4fd43d0f4d8e7cc93da5b58a62ef3de144974bf84b1d376a10fd3dea1f9f6f1560c10ccd14a5bced1b3e4427603c51c82fe9e1217eecea9e3c681781534c07e04a18d2746b1babe24e14427d18fac03976ca8b4f379b97057c5a221732e9fba875f52daa65dfb1295002f414308d57a06ffd6267621bdc9e2777654451f074612feb0bbb4a4efdf47a9008f4440621ec356ae00730b78d4d3bcf89d26329649954420d5406e4b48a17b027272457b9054e02e460764084032f9a818b662fbd7059ea9cd23697324330d54fa2204cbb30523e8cc75696329c00df168507a6541162aa067a2972b26536e95938151ae6fe4ab9d76202de56a915cf5b890227a39f5c801f0d6009a00e0689b3c35f604e070017289f295f08764e4042f552c95c425003724721a7487f690d1f7f3f188ef04b258d3b012f84736b08d2543c5cc2f2620c002e0dd5a9a3668248b510550c8c1e1f0b947922d75e4d0b471d4ad25a1d4445392926f06ba72c282cda3b2c1c183fc5db9a4ca6ce8526d0a4b23f87636633bb63f368df30630a9834516ff6ada05231653f4d14cabe3e432b1c2d55903403cbb26358537725583f33d6670362b2054dccbc644914185bfe27b719a93a861f4e510f7d4e52357e8a310572612187448372157bc0b1d842fd6513028ff026093ed7ba1df25f866693916d64a2ddf61fcae34e3785fdd254b646ed4d3aaa1602ca1d2e2d46138b39119a5c48a433e5336409884eee5d4535aef81055738c7d371bb43d0e48d2d164ae22906d867c6519149b317941a7702726882e682048ca5e161be85cb110c225701efe71b74aca29a0727a2e7672ba6f2ca8fb63c4afe23fa3fe9d36686b497862b83e68738a5d044bb0cc559f6c224e3896612e8d9bd35c4644557ed883e428bcc5155b01e4c61ac1db4a2c4c0532532b1323482b2c883cf1e3c62ac1db4a2c4c0532532b1323482b2c883ce1e3c63ac1db4a2c4c0532532b1323482b2c883c02fc250237621554c8b2bf6b551e726bf6d39951f2fb251237621554c8b2bf6b551e726bf6d399517d5f3f6dfb1a4f2d18f71361d05bba7eaa2ff60e8b6f6a1d187f685b47d5b329fd38a8647848013fbcfacc4317ec20749907080a46c82a66f1f61f2271bbb73752298f57684e0d2286f8b5002f58fc5ee906c342df254015b193d57265c7c33623ad6a7ea2377c181b19d7711a5bf55b9dfc6f3fa3f1d41bf65aed738736744ccd3c0e7249bf8642a4e3875f11ebff0555658d36b9b9c73baeb9146e7bb8744544babe00fd5cdf6429c309251699816892beaf627c746043aeb15f3fb2e8956c84bf544c05f5995f8b5a9a3585f15a191d67e71036c01d45f791eb5c686e626111c0c76b0f75032e71bfd06cd79cae4d1a43836e04d3630c3d6fc02bacdb41415d765663aa17ee423042667cdb00897c7799d6572f059062b69f1072d51d9310778ed40ed656be18e527d0482f4931394373c67da4c1cb7a609bf935b2f01a3775636758c5a9ed67379e2307c9916f52f968c203e6723f52f191034ea431dd1643f46d7e5eab4a70c24e64155bbfc601d2a418477a3f1c31bf820868d8ec4304590f314c718b653b2b9ba51ec05cdc24c2eb24230d9436614834280fcdbf825639c97b5b64fbe97666140c0af93e8b545ac947392c7ac46696837a40d778ed7b537886412d368e6e0e972445aaee687b9b65e65d3a26595044d2f1506590ea41a130ec3da0a26e46751d135e20b9223d8322166106259b74acb37511fd005671bb0a1a645e724824153ed049bcf1a3240cacc5093bbdc576b793fc4e1de07e218ebf9c2360ead5229980de3b538a506fba7cb800a7baaa2a2d6b4f643b00f16a750981437830e52b6156971529bfdd3f4f4b786282a01c026290f122e2c7bd5a28346c4244839d24984bff229827a85373cfcc6040b8fd77cc6a5416f2cbf1207929a32d1a8af016581e83320d069d329de3c51996f25956076ddd7b614ca54ff18174063f69061341572a1f8e75f37ecd688c50b23374343d25d7681f147e13b435bf6d3a8fc065095b2073cd9c6055d1c04c3661f0df1289b46d4a86c6781bc9a03c2af67eca358a4934035e69ab2f1f9ec96308ef5f5edc00800e7bae6a66f99fb83e7daf0e79f0cc723a7694a351377e3f635ecd964c8b4dfc77affb962a6fb259639a63ec6ab7375b51fa7ad323c3fd536d22bac32402a6c3568d6a293cc1152d2bf23c8d39c5b3e725a5802a44362a8004e5647d0adefddd4ca000356b2d9a9b4cd3132e0576631849eefbd604f631127e79125e3fb8418d462b0d43291f4b744b7b4de75a6ecfad2032bc3a700fe402083495e810c1ce3c353b24ae653e98e960d0cda92e3fac17057ada524b9c2e3a114effe020ba3ac501e0ed730771a4092fcf55fb3224256e61db17ac03ba2b887a677f5e1cceba4b414cd01516c79cd7508537271b3c12ec7904950d51bec2b6519e4e474c8d47c62080122c336c5bd0488cd48105d72df7625544b867f92ab955b9d9805a9a9e701f055d5d52cac47103a201c532505ca03578e9694a98b4de2f2f1363090b3c4f66ef19fa190dfb2737825242487aa4e666c755a85bc0df1e292c759b3338f6246d25e22a17381e8d78d2b18556f87ba26eca69e035cb5abe44306c016f6f06f770c8f28218d8445a272e17d52cf3dac5095c53726e636d303712a8c95fb149e26c6f4749210f85db25762f062b53a4971a2bf7cc0d41d21a6190eb45081fc8c94d28b7d27295e81b1618df86141bf05f61cc313065f05d365b266b2571929ad67b9659522e65acc05185b615455725580fad1ca052578f204f3295bf7a7ee0085e2d2872608ff64e0a472dd47c3f16770c8cc5477367f91a6775a3513e5669b3421e044a197e5c1265497be329e1546a1ed4b19928f0d89543b8b7af4b36b0d103484b972e436cd718ad0e1b5b8ea35e3fe86b0b677d458f10953bff6532b9360f37d16b2c761a59031ab4b20ec511cd3386e0317d21071c07be4f51067ff819406695414c114d93410e5ada7cdf097a224d7e501e80fe6a31663ac678baac5220847aab080da4af311a8a9769cc6e9e64da37fc268f87e472a2792864ff8c5f5c5c5a1277ecbc5e4d2fa72d5d338cda3967d1d41ea5f32c77126274709214aa16ba49901ef9134743ae81e2208595c1366f6c3a6ff0c5f15233decd4abaf77f688aa80e4d49358f2b30ffa1752b78d434a5dc9f211a2fdd2df0decd40f6a30b1684ee342317ccde5c59f30959b463e154242f22187178e9568583f735550bb8349c9e911dd43a0e129164c97275989b65a002ee53ceb2941cf06d85753b6424515f9b7019ade5171d750ea7498b7f7b3fab4743708a29d2702ab205531ab01017268a32047a189410f9b793332bf541228355e87e73c2d9708d438a5bea157700da55834254d4985b28aefa66c75d435b85b5632be283255d7b774856f7ffff07a314e93eb976cb6d103b081e2746d540e283170faa9fb1408963a4011fbd6f0c049e614483a78468914fcf6ec2cb3e3e9786571eff22cf00bebfd2651bb05f7ea1510a54fdf73325dc63390a04876e266bfd2e17a82beb5eb95bde4cbbd992665792bd57e4de5a2c98ed317e6b9a583a403712243e244e2e48c0573fb495955664413e3d96ae32244da5b11ab2f2de5bd90f1d6297efd83b67761b740ddd9e79b426e837740ac570aa4ab16260d53b3bdd7a72263d2629208401aa4797b2665dcd741a06308b563df6d9d7291933061e88a55805f27c4f216e45ea56812d5c39d04bcb4c3d24074cc050a0400f6a854fbecb525f59f86630bbbc425d3d35f8160c31a447798b351da7049e58f576696c6eae12341ad3707833e3dc68bd676f544c7f4d24e1e8fa4b691b5c2e710e557dbbf0ae4e3df14c4f83f9aa2cea0c9449508c08070679596ae026b5599181b23d84824855bac16649e54ee90ad855f746b22cbc405dfbf04a4a5e8119acd01c723d02b94e21c55f2f1aea574fa9eb5a7176c5646ee60426659ecae861c6c1910ab1a3514c1691ca61b0fb4507df3ea826ee8ce16fb27c9c0c36d9c60659d29571409b785c23c3bc5c4fa0ec0b7fad5b064ccf950cf9843a502f21b35f810e9248368aee27122611413ff6c44fb9ea5d559179285bbbc32177d6833e0da5b0071abe92b929e47184548dd74602eb072a457df54b25b4bf66041282251530fc1d245d3d35398021e92bb009e276289e2041b30463485ee5cb7c0dbd6571d8e492066c4cbb263d618446aa69e3719dbc5f6c16668210c5b3ba760320131b5d3b8e29dce41153bc095152a91d606124450f4969cf0331ff7d1a431362374ab493b2173ea6e1038aae9b15ee6203021a39910cb085a54b302ead695c44af0acc0b1309b96262712d6d5765a1a73a75d9934b6adf36a265b6b0b570644478336fb2e956c0a87d7ab826d30a96c11d6debb8191d75829413ccfb2d6717813567707bb23d0358740d1a41d268f7022b36e0b9e653aa590678d820f45563a40b75f72bfc734c8b6f47c7828e02ab8d1746bfaaf11ad8536a33966034090b8d62156e8aa524c7a08f61eb8fdf036976bd227af5f400fb7ab478cb666255cc35ba059a4bd65f657a9e2d4363f95bf9ad7f30d770025c0477644c9522093ec275752f54d4e10e7dbb9a793291ce0ca998746f2438e126fc50757b3f2da20323a6167b797d2c77ded3d5799f86a04124229c27ccd6492f2316130aaf17801e1e51fa133f4ac0074ac27e4e64769662e4af4033e6e60a359961db7e5340761624c38232a966441b959a451a74571b35bdb22c3636786f3879df9f5985306d08ef21704bf328562fa403740135421e4324188927e0440517d8b8237d3974ba24fefe57518670a0717f9cf02fc469f07dea3d245e33e7e451aeb6326a7f476e0b85f2827a1989e349159fda6c89a6ed6b107b077872986508b186d30958218422b4e2b25d6132ef2884a5d76c819bf07c5e99c700714ebc75831ed0646e317a3811b92a46e1a16a66aaefc95d9003127b7cc2272fa2606e4b06010b3a3a5ad555bebd5a79d3a6985d11ed523d7055ba35013dde6589111f56a9e4c84c25a3fc106ffb761470a7fa667f41871d1deaf133285191316921fb533aefce543b644f4bd26b363f29015d79c5afb9372244c014c2e3ad76113e8b56edad1b4244ea9352aea7760b8115565577e143655b847829ee89d37ca05de67e8c9dda088e343c2bbac85526bd95eb3cf3ac636418b68a1e54ea4251bac85526bd95eb3cf3ac636418b68a1e54ea4251bac85526bd95eb3cf3ac636418b68a1e54ea42518fd5fb08c697700b08db66338ed60c43c763a76f0f88d04b26da697eb07ab85536b399489ba53f2e1275513e209eac51ddb2df073f4113507a40f2597ceefa1a1959a6781d346e7468e951660049525e59b54630eb0e434d20ff860675fb2077e0403618f6773f0fa1eb120c3811163068b4982a545dcd3d631a0a64f6a37e2ae8b9e70d3ef3f455b03e646869daa73ff260771bf1cbb22070f1ea2b3ddafa66791f6d28dd44143ebc07662ad5cb1713e3d42a6b2268560d8c75600589c7b12df05ebb2350c28340f6c68230665cb57630cc7a58e6addd5f73ef436197c8182b8c42312db08c386b1cba771e1930ef1ef805f6410506970a4ca30142a70d8d29cc64517c189f930e0258c859821d5238ab8636405e3ed301512e825a3436452653d1a526cacf3e65b5defc3283bbff0b9b26fe185c02e5356d39841c8f38ca1c9ea111411fc0c1009e1c21687e2cfc59468f931f79ab804aa2b48f536014c40e575fd0458082797611c936625af5713a8fc9b057a7d585594c90f2622ee1c04d2f9ff774b6a1bf5bc604286cb09a22669ce67c1f6f528f2fcef09c116fa790232f054a73fd0c4a3ff0f27603da90241e0b89c81f6b5c54344b2f446b0db30d2c2334ba5868d973027ac6cf7a9ff4011dffbfdb7226a7601d4f3ac053f42a9814c7a29b62b2cab62e5e5d2c24579b233edd0ebf397c6a30139f612c6686b007465be5852cbeb4ac186c6a30239f612c6686b007465be5852cbeb4ac185c6a30339f612c6686b007465be5852cbeb4ac187a518f469aeb516543cdd948811b25773717bd196a518f569aeb516543cdd948811b25773717bd19de7d497cb496be561ea39f10e9d05e610005ae3c4d70fe64012a212fd4a62b0b7299b045f5a72222b6bb525e7731ee76fc01915497d8e231a46f024ff4ee2958ea973b10edb452602620d44a22b87e088a413170a730af337ab8f43a599798590837e9706236b762c94a4a751638933a4faef475437e30189694a2038fd09a2d6b7a50646f96671782820d272659510d6403174dc761075be065bf748aed52695753b30855352c6797ce347ab7f77309beaaaf783ea9d824b8200413db4fa623b8649554cd5597730c4f4545721b402c8205312aa3f4b46418f0a65b9fb1a11ff342b345718b5d03c732995cd8566d083e102c19d98b355ad56fae3e34ea9267c562d7094c09fe43361d1f0b7fac9c6d45f7c83d8563e7361e77dd3c3e029764875d2c66c55df35d202c1c4771b35d6045d12d73d0066d4607625b21bb6cd80062e4e61abbb66e1342879875e83b290ad5b8d728aef7ab1b55309d58d31c56506d1b262184a83f2fe22a224a6981da1b008ec12ed5307a563f3c9a66491dbf7749b3cb715559036248fc996a3461d2127e23962315a88b453deb910b9640fa485a8966363679e6267282fe748b14d03b7d3b9765ff59b3290af9e95a305cc7479de460046ed13c1af9c441790716c0256fd3e95bbf0f4f041e829b657d0a9421b7b2b359edbf3a79e72f3b308517c77af4e1751b2169f7219435a84699b91f5976c186235b70b607223bf12f3d082664e48c024caccd3618fc150604deff2a34c41a696035cdb11b76aa930785f40032204bb21d15130845db22dc5d5bb68039a209ff462599bc34c4d57904b5134f6b6231d34076f17745880aa3447381e8733612f506b852c6541710184b6dc114676fde8a45b8bdc81efa98752f063621360b75a6630abcb97e5c0983775ac3541875c6967c6e11f1157948ce0a0d46a42d2b204a6239650019b1021360af278823dbcb6f66d7138b3be6bc746081dd290901663f1ba1017c54d941de14e34f4e3c841f9c3ea9fa06182e4cf06507f7655c51fdf520ed9bbc529e4e7a78e04f266f998f6c1d34e79b1f92a1a908df19284279ad04299f8c085d25d2a6609b00dc65817b843e5cd0a911d65cc737b368493ecbc74c76322a414bc3ceb63bde59b94bd4e3d54816190f2eec4270409a5d07763f95df30d955e260a35824322599800c2bc6a56958df96332307167d96b3ed29cf1c140c3c3c2e1271dd9104ef76782553c5d53a10bc0211e7cd65091210e16bca7740472604225859d7903e44f7d655ad116476a225ef7d96431f4e1dc65255d7b92c22ffb48104eacb270f26654c29e177045b3d5209625e819a37e48eea1eab6cba0ed875084cac7b2b2198aca45c6ad0e34b2aea2327704c614983b264239c1139188c10763e323d42781042ac66eccb496396ee9808d3929d0f2b2d212ec9d171400bb2352d7deae26d09f78220173e6d19ee25c3472889f872f253680959f79f25d5e663387e245e68a7da947eb251dc0f987d2b0aed054e2867055d60de2f9d2a48afa133d20f927ecc4a4237f2966115d890ce42b57d66592c748b5702c84e0c44da1f7197f4931abd482b59b8c3116380898a7d0c65e026a767a13b64e25c37afa8867e506b467520abca3ebbd47247a7c5276b9ffd075db956b0742dcc1370cfe9ed15e6782775ff9f4f5be903b372a9febf04d35a0152399ebe7ce010fa03b573b62930a1fe31a50db24b1027055d4a3de64a24acdb7eda1e71659cd697695776f96e9792b034c3069a12df51d853e070fa7c64bb0a1a1452b11606d4d709799a853d5fddfd533583656fd1e15807dbb9d2333813ca186ef0c448f6315529f74f3b7d713ccf29b56b0217fdb8534596615d23c5fdce61773cea764fbb1349d41a1165bbd8134a459c212f2dd32c7c94743307a60d7c51137973661e536805dfa13c6e7305c36108d75c5a737a3e171dd07759020ae10a3df97528987a1d6fd3d2cc754e8aa20642c3b11fa65abc7e4983a71e8f32000f1fe0543656fcb6709b348b189bbf1c6f77738322646b814a520c307130633c4d824630361217c8188f4ae44bcc291c52aa916a3785d125426cbee032a89e211c566aa64d9df1053664b3e4118e49c42030c51c44486bae0a1019136a36672e3cac6b995203e8714eae07741bf7b32656e6f3a847edb1b661496f9c75393a424a97675d3261723804a5db0c3079ddee1fa9e1067192bb224155689b524c5e012f79f9f90b355db0003e769f7be1bb8b7aa0211e34a3af0a2287b9e32f4fd2d9079b474e788e491176b42d491c70bc96013bde6809970b9529ddbd6f59f5bab82be568a213f650d96555da3f0f282d3b75eda59364045f753653aa563bb431fe615eac1023dfa166455534201aebda5e60d34654197ea83d343f39cc4a8d4da749005ea944b2c5a07ce456982c6ca8ba0075c5ca651dc37f48eca7b250285f816659f7877dc97867431b50a179b72ea82937c94b25ec94e33e2944de3d8c78fd1602630f20b06f5b597d9ea415ee53c04a59e5561d42b18761f07e7915ea109f749dbc7e268099f23139a2ae589213e66dd1dac63a9c1a1121cc9a534ac5150f70c7cea57191b58d7da008c177dd021d2b43f03b4e86fd0a0496bd604a10e0884965c1866ff75a6a767a8df45b7b16ea066c774d2a0f534a49b25e1628dd88733387340211bab608761b8414672f2cdc7b4873537baca8cb0380dd500192c5420b9be4681cc5365d545eafad4a24ad9921986c9a55a0a2a851a747e730234cfe127eefcb761166cb70cc9bd354208fa931ab2cb90e632b55516f51b7329aeea7741d5682578797164450c83809708334361bcfa64307f21c2fd41a67220e80ff6e93d4e05cdb11072d7494512010eef6090eded91d01c94b2100b4a64c42d858737b48f61ffab34e03f225ee59d4ff4b2d2dc74e5950439c090b513c4e6b87d608c9b7782f426aec53c07e6c1560a4271fd505da6498426341f2a9fa3bc5c29264e8c58e1b5eba1b72b5b63d36e59cc3355a61e4296f763153398e842a9d68fb755ac9b7422965701b7a14716fbca8f62951c09a4df5a6005e52237a6391d12028c1cd990eff74e84f8418206d7e189e71bb71887497c9251990b33965ad26f647bb11cf139e9e7547c7a83479a81ac6368fe0e81c65c99105eb6bf322e62dce2fc14f781baa254e1b1f45995aa7bd895584f84364810e42469f6ec70fe50ced37c3e4f84b49134f7c74066757ab33404c7ef1350bf2ae925c4d1f2472ba006362a2ecbb405357a439e5730f24815d4e39baa975484c65f86a95576904718094793f186364c06cb006a7325e053b621e6a40471d2f0e75ac12a6b2ab14a1a3197128f2fb42e480077bef77852c4b66e567ea05502ebaa8182191f8ea39d8c8830e87024773a9a11b3f468bf356dfeff6618eca5475f21e664a33593c683316dd1ea8ae0f5db71ec53236fc94365079e459ff7b901c4cc4492e928cc9275a1157716738522dd1f84f36f135196c58ef4d5a4d6a0253c5d1132b1fd7fc01481be9259c5723370cbfca0897090b08b07aad4e79ab0b47a97f0010a8dfd36b62290d38d03e0c40baed921f5939ea6f67b72d40f9b6152db67b06420cc81e682edf5773cd128d023cffeb7ef12c592d285e302b3a8290343980e078e183c850b84cec6b5407ca6c68092f67e8ea6f41c1846a515d9e9104748246578d85a7339ca4cf5eaa59a90f7802ab7179cd9665d801de4368883f0910a1f068cb10f546ded553540405086d3c9e2877ab65f03b80e79c0d889fca4c82ca1055a218ec2cadd3d72bfac1ee0b360d7055a9be03734dfa9a76ccddc57051f4554dcbe3b237569e381fb81c16399c524c1f0bc75c544bbd244d0652711ba2b12c6e468c7f14d50f0d62b3048e69e52c9d64cb2cba237f2a3853cc382b384a1a442f1a026a35d9ccc426b54dc84226ec301a61b0802970c11b1b3bbcce113132a84e759e231f3f7cdc1e8e6c703a6c8bc468d6aaee39bc8f7d348c89b571facd8c06cd863519199ab926f7d3fc2d5c5f904cd0769c77f0f67561d2bdb35d6680dd25dd1ab968058ef6784c86954a90362504fb559016e29dce1ee6b3e15c5c88c80d372aea7ec5364d0c66cd502edc910971ab5d9d7cc0b2cf61b663f76ee7ca4627c9dc2472e731067dffdebd6d2b51541a8a0575235298f870c90c977acdbca50aae696906d34d7754bad67925f5eba42497795b3515632c62129da2532c0d582156f4033c8bd8e64f7182ad5461b8e92e21504c13f3e9473552cfca6f0df1fc6ea2a9fd7267fffc163d6939007bd542266781096cd238210db2c70c37be3499030c96c8588bd78615a7beca1ffd3f920d3db0343f0ce3661dee2026217705bd6b97be5948b158137117cb0417dabf9d6b8a97974b25104a29f85bd96753eecb3b989ae220edb05538c85c71357c0e1f33fecad34dc52f8b38c1149145690755663ebeca6e0c44491c5aebb469c64251229d3f61142e2c2a7675e5e0407911d11877c006416c05787edcbb0074a6bb311657a0c255f37857504a16bb1d16c68800789c841bb47d2c16d3d10f2143833f197a2a1255577d421b87c34f7dcbb93464e205aa66972050587184a735ba30750746091444017e9f634eb674537f681f7c1829f62c307db60b6fca1f5eb965be71f30618173e4f0003c36a862a2650923165df1a6624ab2d2864f62b63bbac5e1de6dd935b1415ab7e60da6b4d047d9667065f063e92b3f52e0490be4efde45561ea5b985dcf7bee2d6203f5384cc07d04a6c6b21734a9712bd3f0475f48e0383fe3bfcf351d64863b29fe62706ea43353f0727301956edc114a4eb06771caab5708cca478cb4d930cf1383333535a9a3e769b58110521da5df3e0b838987ce902b45183370015015d2274286f9d3ef635cd9d7f3bd7321045583a143bce6f05770bca5c158db4c57a8173555ca8256b742ff69028fcf88736760a865fc7c0b46ece782f14c4f94446a0e16a5752ec6e20f3dfed01335005018edbc95adfc5484ebcbf8c019db82101e8f91d24586fbc3734dfc134b8c5f907d192805445f2b110016db660087f18480aebc4297fbba940127db60e087ad3641839f971b26d93095c40fc436ebbb806a4a9f92bca69f65f61f2f852ca06a95d3372a070af3fbf41bbf2df3e59ec465a5bf82a05220b6f43d965b07119424976883cd6430bf2e678137d835ce3fd6861830f270ee6e01d1d10e9e4024b8d2b6983b76914cbdabd3f794eca53139802542d5d320de8ec0e662256be57ee014e5438994e231634030cb8cf455a9a841f6786c20555c04c5f194f39655b8dfa74560c0b836376d1d47cade30a49b4368d6a70a9ee0c44dc6a785a3bb13123206e1f10a9050a06d8b579c42a9f06ef8b2975872f9e340d70900aff84f35ec974710b0a5d5c2687f89d436e5a3f0512f04944a2bc311f5d527b4b5b8db949863ffd184c98df5fd4bd4b2d03205547314a074fe18d2717dea8f76f6b77b937f8039660a9e73a09314e4b7dc7c6f620b276b7327b84524876a5554e544ef265b5ae8538a7574a16fdf44f112175d63f04f9571ccc88316e5a9b1b1d46ca0209a6524e6c2b262e7a5ceb0f3b7674091034ffde6dd86d3a6f41608456677b2707d8fcb91e67a6e3510a55aa752583b07974520c74d16b086f0ed57c2a3106ca4caf9a853488e7c656cb282a3015c4e91e9b4d6874f714bd6a0bda465d68908c75df051470e377cf0fea3dfd72710fe75f54d79a16297bc45310cf69697cdf4f154a3b426f8c73a3464aee97732d16170670090742ed700227442b6a76e98a02749ac1fe140df17925b9762e6ec41b43556dd7257cf1fbf2168e2cce254077266d369aab3ed9a28b5b7e75142c766c3823df1bac536a81fd793a28965e1f3a510e363c212dfe21400c994fbc5becf2ae2e52df0c3569430b57e0c4703e25e288083e4cf44631202b3d2f406f1c6c86172146363a1879a6f02252aa7d1ac6bfd65922706f524aefa415ed322935b98c8376f7e6b12ee5a7673e9beb6360cc58c53b17047b66dffb7c25a4003c2b20333d71e8134f0694bad2076c17b04e11f87d70c0692b678d6c6764ec607c432226db5e84a501443b99e5278b605a55e8a9ea7670b8755e66bac8649662d37e70c0d77142cb07213ad2724a5b4a230bd6270f49af3a4f50b4e1593098cd9c766bfb8c690bc4841f9febc6422616bc73c029fa360204db4db3242a303cdadb3353e2134b93af46516ac3d86966bc515d282148751970a96bbe2d604b872cc1646695bb0a9b7cc53afcf563291e1599151b1fa83f3e0b82741841c15b4943fe2dc26021471a01e47934d71043bed51d0216e6c978272d4113dbdffd7ea0b9157a3e9a5c0796570e429948f4447317cd587daf42621ab11a752b554f7e26f97648bd29e100e2723e3414a98c5c2b86eb01380862542be5ca07fabb4b22c20101322790b605ac80c07775e1b54637b31b79b84dcd43a3c72b5970cd1156f779c15eff091b76efc609600a28b43ec914ab09870bab0aada8c300dd5362029d1e4602e72ed35ffee2a52dca1d86227b4bba36bc48762d51670e37667241123a5464010c8e3344c670a6246991b0142fb3c3682a85dd2af7b79b65b222c73356bcc54b63b1442d2d0f8022e81c5a575da1a702e167941fc7710c56435ffd0e84022253d924cb1414c3f425c7aca3506ac45f63b729383e640a431e60004c225791de39dfc93c49a8f6174211703757b224bd79d414f4200c730c10dd1415643f5a32589cbc6e4395d41f37aed80a25ba71a35be401244caa917e6864143066c18f9c544a206c627180385a0c7ede3182f2a05398c08e01dfe4db20870b884bdffd303a6269e14d1732a064358d8a425b0fc13d242810111f4d42682735ce0d1f34a676a7d0f1058f590630e70506641e1412328721ed2161bf2639211f6c56bee1a669837e8005cc0273657be3484dec85ff4674ab12707d5737543592cb405f210a4f89b9c45ec3757b0cdccf277568adcf4f69078b203dd27908ae26117d94875f34ae9a0e50e79aa61194bb0d0a3d743d1ce7df890317d120635c0eec4005621a1445dff44b3908054e79cc867e6c294e6cd02bcd514b6c29171e6fc3711e96f351ab4ed146b0f78406253e6c2a04d65d545a66e20fa669b37d99c96607e1f8ff62d7e9a1246ce8aa0fe2eb956563e0e170a7274d21f13e1021b98a301153f6633bda240f03c5dfbc7326636a2149dee30afeed4549b66bab7064f6bd3053c6761425637e3444fbe14b4e24821a2fb34d450effea01a20938678a7da109dfcd5f4ca72ea32099465c32867be424ed9b862da228455f54d0ed4681e4392c551b1b40f288656daa1c9a61edcffb5ec2358a2f7683e65613c1ed466576273ebd05597eff0e6a3eb6c24767ea82e362ed66395c0ec73b39e9c0d1430b53f33ff06f6b65ecc49576e1dea67d7ab9a17846dbb63e18a9b20e9b03b047c6b9b63d17dc2a21eebaa053e1e71a0ac8a3ac54aca5251a7f1869311777c653a72b2c4c57061b1b18390e1d882f51319d9d75041513e57b7e87b2628b67a43166fc220266a73e167577c55ed9a90e613326b25e14ed920c23eeea2c093c077dd5a6e651dfc2ad63fa307f17360f5457b699f50c49d4af45073cb146e04281154289934edb5a5a5b22de9e19219b953c8d72672b0247c353a522d916499246758f3655252bc4f60459ca824c3c8df2762410975f99c3cc49c051730cadfca55c5034533db63b2207a42476410d9e34118bfa486be5e888681e87c412a1cd9471966ed476c063f97ab1b70e4aea1d825b38158e75bf47c4221bba0b225c905f378e5a543d7726ca2a63f9432084aec27080121d05a7793171bc219c586cf7836f589eb220a094f9419693ac00eff01a1a98dfdd1f64f1466d79ab8851b8a94b4429da1a3fe74565353ec1d82c141fa05328e1cc7b433c474849afa67b2e2b014e22cbaa21e28e4f71a48f6e4ca6bd1213d205d52c2c88cf25b0c8e02dc9a4c770378c68472574754ffdc0be1ba66ae22d3c27fb6ed219a360f108db51d0c130225618de1f0a7dad5fa8353c0b4175540cb90d593c80c50646e056760bb96cf27ed2afba3c7418ed3a0af38102df7029478e356b6994874c4f60c4765ce186dc281bd86b6126a1ba0f77d18b5a58e59721e9a9834b48415a44775a91379554ca59fbfd5b4bdd32765d2addd41244b6e906afb56a3b4ebbfa2282698a0ac65f446537bb550e310b7f49d7c6a61390f47711285e95026994ae672c7c9369712c376ffdc73b37aaf2a45234647c4ed591e1347a66764cdf5a465a2f504c3768c79c480bf89b0963d31f6b37ff12387bae255324bd6b79a9b17f566a94fa7a4ab47341cb8da21bb8546513d98cc57c7189756ad62e982b8017d876f0c5ea2ab1be974eec84307dbfee97657e348769a447837ac311f43a81ef020a7096e148120f15211dd88443ac6e8d7d6674ca333c80a66311eb505f8f99297809466606443e914664394e5f2d186841899a7f781413ff5ba20f9567e7c5562f46629f08bc1a4208d1b3b405f904d150e24861005e680903b4680567bebf0c52d3f2d2097c365a796028dc370dc77161b148686899060e3fde807f44e9b8a5369247e7715b58c420170f0e6c63ef77195cd5a31bf114c957cd18037122fd6b0a2ff3d12d911a847bcade606d802987182ceee25ab20367574a282d338866357b95a18f54ba8ae6470f31895be87eaf035c550c7afcdc973f08e2b749f161e32ae27ece3adc81007e368c17090546be0e80a32246f5a2e725a41dc82981437454514eca575884e26dd9f8395e7329ac042a718843949bf97b6178277b0873c548f3a69d74676a974514a8955305a4544d50e33a406ed662351b342450e583e958824b4f6f452cef1cb943d82c98e81812b5f230725122d5603eafb97602e4df479e35321a872cb126419b5a697c5e7d5fbca80f6c45b5820b8354333bacc31a0fb0bff21c498268319eed3f215124235e2a30ae7c04be415fa067fa2dd35ce751849aa209c6c7c33a1daf363e1146506c9287dc0525c74239445f24181367b835ae5c7f27b331895bd1cc5d5eae9d182fcbb7333870f526448e44de41863f5650dca95b7a93178736e901eb494a2e5a014a8ff30411e8146f9c86ec286be57553e17b6e4c831c6e36cb864461e760ea343e25fb0fe441f809f0c12d7dbf1ecf560695ca63c674df1d7257f875349c1c3eee3d2c5aea79d60393168d0b390fdd17230b950bb41b617086fb96543767846b207ecc58a10b8c1e10a4a721d1505b08227cd267e809da6cc547f700cd266339eb33a71e35329f254e24bc05b948b616a8eb984705499a13e9ef602f65fe2f529ef1dc6544ab9e46da47c26dc5a207018ec0a22f490bf829e5d2c15369992f2c8471970a3d323756e07f331566dfdb7e3d541d22d51d093e8d47a1722f5ef55061a4d244e7990716ea2f4e21f753e648205a0f17c01c026adaea2b5aefbead3ab0364c717e694b26d8c3ca282ed7040441b3ce14083bdd18f4f8952b336c08576d88ff275969a74a6c9d594f8629624166f7db14f0755e10bf92907882bb575379145f1ec242f40844ebd14bdfdf236c854f295e8caa42697eea0f00827a697981b4327681cab82ac5e3ba7a56854261dd778d06af0f9c3c50724b0affa30b4876327060fc7c4c2cd4aaa8199d3475720116f6105290ce11f73a906c53b5941588a6bb46cbe32f174254373fdcc4340cdfebec16615c661dd77757730ae9af0ae797035e0891bd2869fcf700a80eb43eca41ae6e3845912208a88836a11f806b04ee4c62f074fc52a1324d446981d57651e6a22102c86e0343f6b510e0e8127951d0972bb06d1458af59eb5e53c1b271c2b5c45b2366c42ff3df88365780fd008d3c2c56538e6e37e64d785c72acab12d8a979614debe645cd1b7b09be11383c1a8c9e3c906dd61eab56cc728b8d1e2c2abedf7bc5587764c18eef066c0bad4589cc495e44c05e0178b86c0126d95328b6b6956ec5e29351e3c8da55029da053a7f3f51373d3da485e248e50be3e03731e4e013510a07030ef9f25488723283ac1966250c418e44bfa1844301beb4248679d621c3145633387029008253ca52a44723a3172270c5ca941ba29f1ec307749b3730250201127c8963d2848752a33f6f9667171e1d928d9938104514f0834b41080405d9c07533d8009795f44c3452722112a35413c649d56d16eeab0ba6f4373c9775f6e0b5a056ffb1ee507222908a50b426f2d790b3eb0b12055bb8f6d5d45423e6e13df1bef76805af9befe0d6feb412a0a1f4e766028f32e4064cf148e3cae62ba99a46c57bf017bd4f776723cd9452784eea318c3925f757674db643485e4164019375a7766a16c49dd242760b92d47b8697676af17a43c1055305ca125ca5fdc3d6d1c24c4064339115b050a4ba7489170701241a77927da73eb64a944cd4a101b6b56b3a2d049ecfa9f049a9d955be2b9db37382d3d3a0021714b3d384814524ab227b7155669b6d1e843223e1f1741b4d339594d404dacb8c701aedebe117a5069233355b243e5dc5066e3b68c6ab4bc2174475f670db0f0380db716c2781e3f2072ebca9b594d3d843e446fbe5431433d5441b2595a2954d24ff3b9a64c7a4d674362c91e4456fa3d62b72a387d111b2b0a0c30355b82036d4cc85e4b12ca3e325e373cdc5a0078c33ab416585d45a70c30e534752b03f3d31b8e390627952d633de0779c3a35417968cd513d080969f562253fe72625ef6a612f98361969811507b871ec62b0c4b93dba6d1f5535281e04f3b1cc7cdeef9e56e26fc03afec5da3eb7d3bf61ccda0e04423e3c58dfbb88105cca41537d5a2f07ed0d304184c1fa57c95d423197b0174a04b0d95352eb401c827dbb13ec37d33562143f1d1bc92949edcc7e0161b0204c32c7192acd87b14d2cff781037bb9b6e48f6844b599e50068d6dd22dfc59f03549969e0bdd970e2a83695e01b04bca717ad7b25792f87c3855af0400313ff6506abdee122910e077e08b025c6938a16133037736e7bc8c4d4f8fb36d56bbf24492c36c56493d2b5e691b80331591e9142b07760635b045690028883f16e8862ebfcc137d9326bc661d983936040920114bceff13c60bc956161b93778a04c8282d4d5b4ea1eb230175a54f60f0a78740bedb1b3dec20aa19ee60a30d72de0e6b72a08c2ba7cc520a9da94c1a85b4ee35f4dd917531232f49b4330a431b20f665bec9fb00ccbbbe70ead9fb45d2b7616df854cb35b831f2569e677f0ab94c79659459f44be17da178cfaba24cdcfa225ea21cc977e4d58d72cff3b05fb8c802070c2b236637eab50a28a40c516e352223b6b7964c7c805271d322835c251c0c0049b6f5465353bc60ede3a707529199560962e349c845956bb21f8b35e27ff230886b0c28a80158525c18aa4495e198047f1eee49d14cdc4890bbf57273e2a5361e243a664449e1368bc9853d8cab0a6da0e9ae75a9b8d83984fbfb277a61c61fe470dd512fcd074ea3f03e532d480271f85b903bb3e9425df230842e9edbd624d294a355cb43b2532e2327787ae8cd62b65c037de9aff25844509f165af9a23321c7a230fc782622b7a2547b6ca56b42758ec81984d05d68cb50231e3bb62223c7119d0354de905acc798a57cdff6064d2c0fb57ec6e343943d5a74d73663a12bc8e6d093340fc631d520d6e59fa6f55f162a35210fe2f2a0ddfc04f36dea84cb698df047ba6f44067989312da02434734719f18fa050b676ec10e259384d92e846bfd5142cc8c5a84239023fb2c5571037bb22723c17844404c2f4da0b0704bcc04c65e094c1341754f2f67b3de2c4b28089b04308f86256e225106d36aaf4f5e92ca519d7ce65cfc8a7466414e7d0989674806fc022905614b5831a1540b67b442cb3a46cff31f943f814236e3b6343e9e4f1a335207187c238e6849da173cf609764b45de23010939992465532a3a832f846b566ba23530f7e11d584744757992260a6fb42717a1f790330a5fff120066715fd32b3e06dc45043b15332c1ed03804272255a14902e0db5ada2daa3cb687115de6fa7056263d67603c87061d828b0834cb0913550766ff742c540935b1219e4f9c20c118c2da3c5907b7a63879ac655347d4867aa0717e0d42deb80b88479548ad68081104189035d1296f0594707e3276328c19e4dc4527531ff82646812c39a00a76718c201c3df5b0815831c7e336ac7ae53b68d0267d8328093599b2965e84ae814a084a3b01703be30317767e56a41c6d005cd97e5ef522384ad009e95e7f2735299681bc4ad7f5e02c58dafb6654588c32b9696a5079d9ec0495b66667ccfd895cfe23f673900b592dde4b7c1ced74e319c6a1b12baa434d4a06e2ad5523266a28d607d070a75892295332601e6badaa612ddc9575d63afd4071606f1e3a16163bbf10891249cc14775e616557a2b6042a15af8a4e2bda737a0fa7ce0dbf6880126aacc9542af6255601f3ae35437f536e5b30a14e623e9457a0c2202e6aca722d0b2f1d62ec45b2522e17c32f0e0aa91724621e76c17edd7517ba4d559406ae451cb0255b4b96d444cee08d452daf170646aa525205fc054f5154c000cce9036f4efb4566d10634232e58d63e23e61b1a035a033700b2f576877b3a3b487811755f862d56e412610aa86fa75ad00f9778302d67367cacb02cd9c02419e0702c41b7890f4a21f5b15899b9722d8e719825472b3472759b8c6a2349d802733dbc6b8544816680d5a1284463ed46a8a06c1848d73a5152778f4525128258068c343d88b245095f90660fb41b620021570e0e3d2b6131723aba362198a116bd6ced34bbf70a2341aff27e4918d666d3bfc9099abc567ca0054b4d0ab80164788b5d375fc5aa580ff9887e5f105f3e089ff0438c759f67c4679c41ef67f40934b6615cc9ca920cc4f9e83a2e54fe35e43acb3d97b9df15f50ba24ea5dbd551f5b60826187d893031812255811fe333f7be8d18deb7182a2fbe165b9448fe51217df0042deaeb0915b09d6c6010223c7fd0521b6c529f3402e7cd4fd7bc3048a32873702c31854257be03735904044b1017263ea066fa0b6ef357743f0f0f5d1ef3e164beba734e23a931661225345878d22a414069633016342e546f0c5f41996fef335d9c842202ef2356330db8581da0e2697c87797d2b8ae7484afc342cd21a252d31c8143caa0f5822d30c2e325e5249357f038612b92f8a19b00dfe0afd17a0200f760e2cbf98742c2f198725e3d6770eeeafd51d8eda6401e09f6f1db4db3d0d8193892b86094a6b22b37b68ddc8c6268c71fd675a2d9d50f45e3c73c0a0c21e9b8d1833f16d1f1745d91b7573d4b458a055813889c8ff5dfa547b6bf2483548e939230f2f3ad85fa3d41d1608773e671ea6f211849fc341bbe056481d3c1967911d34139cb2cb5c6fae2a363fa76c31cf1fd9543eab026903ffc932b657545aea0a246fe1ff131113ef1856ce888229acf68560ef312c3af042014395c1f556aecdef191dab09161ac6f84016b14a5c2970f8771ea651505e35905ab519f019e0406b507955ed7a0a7cb76325d4c84d1db8cc615fc7c904e09e573b85d8dc2f3fc017181529f235ac43b778c907515ad0ec372d4941c23a97c8fd0c29a04029cb08c125986abb67c932cf668b57c827bde7817e4ed4580c9432ac11ea34d17a934eb96a0297b014d79d6c5d353b7b2f3539cd6dce6c8f496270c848248d055ce0af5253c96d852cd7ca0352c0cf427e6529c15491075f6992857240d6aff019acd6797a9dd32166b9b2c67ebd864d2bc5ccd43ee8a7786edbf42d13a165094f0443e545cc926c3f0fb4ce714045ad0597cefe788892321a4744bc0065ef7a1397ca2f14699e7862eb516d522886932d710f7865a7535b6ada50d40c32599b3069727a115a66e6747de72c5c04dc8c271446c70411c29f68d6256222215a0765054804348eb14c4a7258645c3febb81d17999d4c9d1cb840863caa2cd290fb1ff660405ae647836f0c782e60ad1d757b98cebd5756ea11391c23e66f73167539a53ee17ada50f169e9f4610e3068617ee38da81fb6e014766e481640c2e1a7083fc34217a31dd7023417ae3ab2db15707ffb7534be22d6363899b96201159d183613a52ddc58ed155288c1225b5ca47834c65711f880fb0a250f0929d7374002269dd93ffa2e9f430378e1277470a079d701883b0ff261563ad23d4b19796955deb8ca0c1895153f37c342058d3da20c3515933cab0fbd3e991f386637ec786af418415a78272d45e8221a25d18ec670af74b77d2b8b862db6c6a1233a639b51f089b7511ecce40665d60038616c80599f33f61f171b9b109e8b27321f992569d74b4c082710a43442b9145d16302a607c0ebe720d6d7c44fa44350e0a228f012fd17874ce400d08a06bdf00a4f9604bd37027489186a301e9995238cea52c6f8e3b51223e73283ed051e726c71c746e31b8144669ff5574dcdb8145ba1707375ce8006df11cc35454bec91228d6bb264780540fa4e4d24e4826d07defeb353a99ec5d5328c23c0eb7d2bb7a2d64d44970faae6a9d2e5d3fd5391d1cb4a7dd7b7a86f656e7a5af4b179dd83402745e219d036271e7f66f76163ac751bd337c24c4aa912f18f79e7be3e6e635e731b46ff02d987b53ebde70c7debf39684ec63cb55f8168d24a9d32f7ef957de4b1072600cd7b00245a656495c51a20fcba573777211c47dc378027ba1a4f6a063c0537dd6edf7abe518f4055e47a1ecaed2957c7558a3c5e9b763fcecf85799030746a6d1c173ad7ab5c11336d915bbdede93bd66cef6c4040076c506e4a5671f51e6339f43c68faf41b2d61883d133972a644ac2c2e48b6c974732d1c3375e75cfb1d5d19f4654b74727e8060211c023d32783bc6420d4164846e9ca0f63ca75d053d7e28bd1e691bbd6d25b6b10bbdf76060027d70592e3a1541c51e1a0a47fb716604fa4e499bb4e21dcedf7211ce7ba957ffc60576cfc2715101bde5198437e45b2b4ba833fd8db42d09a01c24a687d00c98585b0cc8748113118bed1a8daf722ae66e212f27703d561e9e532279b1184d0fb30c5747e27d4581f11c292f184872405b293a42a45f2b66a4c94151dca52b354f0f6474af482fc1ab170f827d94364164574d92d3bc1d1f33ba66a223710a47717e60ea9c2d2b1f91b9034ad36d7653720553fa1a5a4c12063668ee24d701486dcc2f2d435c2046558346c67ac92627c7502fb9acaf66da19636be2dd1b7627d67f63b65c322b02adc57be16a2370c4528c3d59e5041ac844ab7d6a7deb1c8208e96d0ce63b0aa35fc60d9398e424707c601961b69d5a647abb6526f3ed4d9738b046b81c1667853a185acdfb9d7eca9e585481a3aa19650e421cc61f5d5c6b78211a4b68fa5cc1adde18a9f5675e933ab53835fb007195d18025eac90e1d00344f0842852b3fba4c0233a95ce27d5a857349e11194404387cc62cb87bc2f7e31e84501190b4d7b6b9b78c23d680d9ac77d34497e24615c74eb0ccc4ae92e1c25d33578c3583fe8b8ba04d5f7e129e2a7ad7d3f5815090e4cbc5419f4422c97ea1925275dbe349a77304048d74b05d849e531160cb901dc47272b2e461355bbaba21602bd0420c8edcc21b244344bc2a7985c255ede62ee9e764aaf5f813cf129bc3021e3815130361a67535c1c375c3af32da4e0515ecd3b3722cb0ef414b6e0ae04c50d392d8738e145b46b232e9d2b6f2b1b137c1f40548102753b403ed34b0d7c5db0da72458a884922736a741deaca0bf402c46dd29cd152fd985e1aad58132fe4fcb937c2be7743e76dd103f124f233d1a85243398a7e08655a7a4c83ba8073f04d1310c22e3456980990326652234935d2466e52523e449b57f6232ae1327df77a9402f1387568cf39f16d907dc51e3605db4100952d657ae9a45d79ce165805df3d357c626e587f2bff72e9ae8f43843010097532692828c309347299ff41c976b021c4d9fd529045d775ddd5485f991a7f7c61b92e03b313ff3f8be45c677b81ea33a38d596f6fc8de4337adb727f1776561cd69885104522a1fee8fa200a87c3426a13c354a2711177c88e3e6268ae7867b1067a162aa710534564fa931f62dae6c5e601b558abc610ac331c0246e3b7c298e0bdf3d2b088738809ee803a31c6265bb35f078cfa35e228216de43b707301a9d33ca359f399a1dd4196419f847c94f01dc9b4add299b1b1d947f3c2317d822a77e8a4bcce9b635756b07725d20b105abe6d629fdd3ff3dc542ca4acf54e72fef031f3c4a2c2810379e090cc04d4726557ccd735b3c3f7c95db1f7982f48543e4be8904af68a23188216f58e5061270dcc50515c07ac5292df72e5feaadb91cac944e1481b1573f330734280786f902ba33032efdeeae516777832da6ee3b47f29ed4785ba8b362be7aaa0624037529e711113d08156b7de9d18d7d76c0ad01dec7c84adaa6e406832fb069764b116a43cdb26cb3991a6086d21968acd03942b4872c2f51b31c63d06fcd1e45d5da31e830581a2aa208540f04ff56b7b6b80cad636a560354200e3910824c03579073f9845866b90b3b753e565d48ad194e59e75ec439b1c9932ae0e83178b14dd808d95017709966ce1d582a6221e7c82a71a578485f1b0ae9382351fc049400a23d61ebf87a713e13243270c229e992d406b2025634f5b7bd0508e5b001074851732a8c3233a6a3d17b78c13e439aaffc11e6a872040376bc21e83132757f5d4f4cd39206527d297f648827fc064673a254d30b064c98e1be36aeb5ad2fd10bf92707162e1621225122439b884c59f662303982e2000644c136c495711d31c9162cc91eac0a726d264fd4a87c48d5fbc1023f55b749bdf3a60bb946250cd5675b7d613c64724d07d919d25d9c703f29c66895f58b235ceac75a98273617b015e72ff80cd0216e075f17a6998640adbbae668a9a313dd4931c764af3f930278d52566e97e6733e1ca32e9dcc4c46a0ac223bebebe3136296240dd188e70ce538f04691ed5907fe9fa11dd8219b5a607cde5f811f3911563d8555df51116b07f5945062e6ad5b67fcdb2711dd532d67a97a4533cd0a7cd43a391b30339d0db77e346a62283348d50fc318002e0b651922337dba50d25dd15cec0515eb780c8dc6280d0961a24df0c72d01a552461339faba5ed9f26c145ceb0865ab3bf41107bb815b31520a60ccddfe16e4e15e2623167519f22f7a11eb3b85146494166393e00000bfa51f7c54ee2f75d5953f7211ce2d47ec43084feb860e084f46660b85eb965f008d175d7ca14e3238a8ae0c60984a41ba3ce90331d90270b02fd5722e131e44c51f8e31ef1a9b1bc949ca5b8436af011c971056b963bf33660a6a307ec8bd0d4c3d2139ccb67e32b2c18e0a4ad1934991788b281d12c55b1490b015662ce2663972db4ef87f191567af2f66a267fd372ba7432e96d44e2f4499c101e6939f4b041ecd75849bcc335dac14787109d763d43c3d29739755266617bf2526c68d549f40b85441e51674d3913440b565de4428f583382d733c117285df6f8f2a760bba51034cc3fbf364d7083f7d3bd4e4008ad4176422e8e33a8b26be267497964e7684572cd5d6c6348e79df3a58c0ab409f505539f6c955562fe15b6bfe25063df281672e65437c5b1dfbaa1353ed7b680d5a0715221c2d08f4e64f558f045b041c22b369bb6eac047ec3084f6128954d84d1df4507ff372433bb2b0686cabe7c6b6ce8330722680bd9e956443c9211475af3583f69b06709eea43a3ba90562192ca3be624687e56f03a6ba613afe8764c92139605a6318428b756678a73d9d559fb0292eb992486365997943d02bfd10d0445a7e58034a0253a73d5c84ed6c2244b3b64b0efb5b765f200819c31523585912720f28aa0b63904ea01df9782f542277e03d99c29c2428e1e52c58c29360d7f3c26217a14856b5abb0482b44404bccca6668a0b63c0fcab8a2046125b4569f99e00a62ac895cd4d8b67a74a8a222b9455d7d62c5583548386b6f8ae239459d4acf1d941a335daf5fe946916eb31fbe4c0c1ce979b90844697c7aa584a8182e7e76583ba89c01c5975b4d7709f002c486f87d97323315e561b47e051ff07b1662e00e0e1a6f274d56e537684cc2281d602f2fb6e9310a80df32402f23c44abc970f7d3161551946337968b9f2b416c7076a6c3bf623369a862d4c5a75ae3029d18d4089f6ca5419ff8b319fd3ed3c9d10f33a12eb98671a455867c8e59c58e4c1ed7dc255c85c11ceca5c6bd080150fe10662e7f3e457a1ae436a08fb292edfc9ed1fdbaaee2f65477a0294997b2fd213235366944b69c67c787e90fad1716cf18369821d52092bf7c729225e957e7981f5466ecfee589698350477f36a272470fa1a5dbf1626e1757873cb1d2e47731c742c22c7b4118a2ff664983af61c6f12cc11453bfa1a28ffe640dcf952321294a808f2b3f43bfd236244a622152f1ef75e065fadc70e69e09f28d22fdc13b973816ac4ff23496b28c42442cf53341af35b2c6b34ea019999340545a05c181af31f3a6b0c0d359f53c379c626d3192b46973babf80d18131f8266219b4b7a7cfd75140687da52e17a3f0c5c996e1f54a82f5f988e0d35d8948b05fcbd281870105b472d0aaa3ce20be804af8cb44a7873240376c216732667e527c7edb96477e7073bf9e61b5cd5027b40d24001269ff4b9723d8e9e563c4a31043574dd03d8a0851f3a81194181a6be6ca3457b56f9c95b0212863b4de016587729c2cd0e8525381330add561a1af6921018e6840ce43ce4d9c71c2764643773b59dc29063605811cd5249c580c33537907dd1431b08e4e5ccffd9916152f490c859a437b60d7b67882f8836c4c1a7070000fb47d7c7ce538bdd5eb485baa9e00b939745d114eed26b2e62d4f41d33370f7769e059ffd21258b3fc946184b8f7157ba1c54e263c9517a2f2b65ef4d351e473dc140898e48584423912a57d55f145a9c6a3098293b61dbfe5e18edf62b423a9e6e7cce19bf6b772c893518f9451f3c458c26453c2752a7280a4093391e260fd3ae3afe65bc0439d28a3aecf80373c14cd237786b9619259eac1fb0ab3275060f2b46a442d64d743bb118e862344396349159480b1d31df8add61de0bdc5277102c08905f9833c9d35f5a0daeaf3016678c68972e20498789df3c59565a408b6a65584d24f56ab4d7222486a1ea3ecfe48a08f136110e8e770377aaf66f0c967c8e5fab633876cb25b22daa863a74a207fb41f51806604f1e4511bc41eb5bd16b4d1d53f83c47d32998680637f33c2550cf030d8029623cb39c2fd1fe416aab68bc5e16f8725e08479608789cf10d32ffb05f4d57ab6280b9545881d1f758288ee53a54de7e37a56c985abb4aaf4af8de7455b0693d168ff642572e4edb32a06be45969b1294c40844d161887b86e4dbe6661aabdf06ce5dd0f020015905915a75817c5df2f5eff35e12730cdaf664e72d42849a2536e3ecfcb1dc7ba8a1f61d6aa55fa626c65780650201a2aaf46da8da032f4195e49cdc5fc761df8a475aee7373d6bcc9d373bb22c0f778fd32a5cec721a0465053815b85e0c7d46bd1b606d946b568386531b14345740780c6cb82615197fd195030cb33d4bcfd815235abedf4096bcc844b3ddc533e5895f13469b6828df28de2555d18d4b26424a7595a64f259325c72fc5b3f957e843384918f06b0ccde402101a228558293ddc067b906b559691ab48f315a36ff341ce6a565a90716ee32e44b94716653e5f05229bdf673da330831229a3b17645a22057ee91b5532598ee3e721a4849af337e3fcc01470d1a1d4a7aec6e3c09c036727bdf0e4f6dfb05cd12e09af10d18029b7da089c51d0808e3654c559958321e4e65a83fc52d83ad3c6506c8251ecd08a129d88a6e7a8d92412c673a332940b2340295722337669ad167be96a11e287bbf046679cc719e444d4a5d9e155fa593b840ea089a5c53e8f136e1fe163e16547e159b52c21e9c5be32cbcf93a494659b70f7aec423c5877db598ff74233e9e4601befc0983960ed875193736118dd12d04687bac153fc5edc2b19a8a8582d2b4b3d1771fc7be682832ef7e89d3cc844cf6572212a3c922a6a0c97afcd5cfce2296ec0788d2e8d63432b163cd0389934da683470bb04a256681ea409ef4f8b817a4c2bd4b20496f1071a5c324346721de55baf5d680cbe129451c2cec4712c5ef863b1ce8b2ce99ff1567f85250f2e47f5551f47006af17c4c7703bdc95070cd9466e4855a790d42dd7733d5721cb3725845b18dc536e25a2f4cd0e4084d3bd4ef470c795111ea51b33b8b57ee4ebbebe65f00eb4e1f112a7b2a560c3c15bb37ff17ad07fa412faed96c01de120f976a694c064b890a06953d6e45641b10f297802b62463b26bfb2937c00cd256b5643f07445e34209fe69d54a2e8d644738dfb00bd89929446649da4cdb7b9040046b592db0da555e7da07b35d203a96eaaff1839649bc41abf7320358be3965bf2ec217a59645f00d8131f731b625a06719b8c76bf53342f294cb53d37f770109846ea13233b2f5c1ee2e0091ef0ad075c55cf1b33438754f9b192240fa9726248f2c32c0fb35009ef9bda1c7d3d6f44e1292f21036ace1c522bad3a8622f83593c12d10386ada49a216c51bddb57b7154a1285fc7e6ce18086b260435f1f874a5dee90b3ccf321bd5b8e15f353b335e3e0b2e753d1c25751397c522378e6c45e7f9865c108f6952396cc20ecebc13256a6605537e03f3675a82d0329f895861ebe670748b90a8028f2af4193b99fd53385f9752b52a6c365dc88f76d6416f77e624f0798626f075e6a3d0096cbb5946e22d215df339ca629d415818a252775d1afb6322fe8b693df8605f4851857c70dae66c05a7babd0ea7c837090619a07912db1b2bc478f97102175466e91ba42e05639a5623d480521d204a3ec171fc21b4c3fa405cc9d045e2c7890469364869cae9b27d20e25c3a6e2c2b0db6e1232db8796f2c50be6421cb03ae559265ab5fd62156077cf45266837a9b7d039db12dc95acd1ca2afb828e083e876c2ad4f40e88d570e07df8f03de9b6d788a302d454ccb60382c97f95fe3c9d66c827f51747820195eef89e32c28d89b3463f51e442bf2a84e50797c1771f25d07817f98513124151b4f436b3d8288821488dbc2498a8ac76c4676032afda09c264addba397d515d57c941a217433eef113a2d74428ca2c6705e33ae6cb745402eaf02517258389a772699581fb8323420974902306a3f2106858c58690e08673e5d959a16960d827d3a2a81115987604ac5143d7d6b03120742bb283a5f8ccc0f62989a04cb0cf13e70ac2b2f1c13a84424d71c0493a4095d3884a712ba0b5e442196886a4b9b1e7e4696bd6335567e3aeff0b17e3804aa33059384681eadee06f8c94828972b912c4578d330d2252f5a7c2df91d8152797af7b38c0bd5cd527d89601d0eccc4a36484624442647ee43cfbdb5021e5d37a4d2985652c02d7377ba1e7420c07551e672b3c56147ac9ca384f12de63b405c8557970b12beee9e7677ffd6818f525716a8429a438303cb85a3d87ad512f19881cc026563e36e3a1572236ab5c5212196ff9a7ab1645699565c15baf5ec19aa45091416a0e87c96c56e307520a5ba6436e74a84b5ef4bb01421a0fd939047b713853e6ff7c902a8f5caf42987cff11795feccc146b52080449fb42a5350d53306c197100701fdee776d3de6b15192735773954a73141cfae372ff6be57ed99d603e1c33d4d340b3f1e1005d1320de17f095ce51c3b0b6085374e670b5234f9ee2e3dd74e760f753c145e248251c49afb266a091929893fd20749a57e662268bd77c7e77e2b7951b943fd6671619f19f2768308a87cd1b91a746766de567c3f893a207031641a98320c9eb53a32bc4e6c77cbd969751a4998062e4b9029b11c697166f6a438e7a0c81bba5ae73f50f5233359b74c15a341cb3854cf0f74950bf575cac2fb67d443275aedd84574f2324477d232b072ecb1624367725062c433d52bf247412d80dedd3908e75a4952d8ba2774b16d4922950b28437919624a8e7b0ea8b5c161f7c91831dcd03d324db4a04f0c25fa38c67def61a90d4368bd597c24fa91376e6f8902521be9b21170f1b30591f83654b1007b6500c65624ea30515d1a910a2b096e9c284958d11e5b577d33db5933245a32f46d75c3275685518730f074443a907b522d51886635d3773c086ca8b27cc7f6d566d4c39947de75a91a53eea8510e0d541a7524017113415e24408fbb3ecb61721ba836f8026887d31b9d73af44f18c6b773b813924fd672a50e6900c0c1e82bb0ac0dafc55a201745364dcb00aa5e4dc225ca919631cdefe18b75bb10086b53a18d913883874661714427896586804130f868c1514284e133a5767a54ff45ad750f2fae14e83cfe1297554991e6cc617605672a53a28020a6a6bb988158729df4679fd42634203207cc7ea6268111e182d99432e4b7a7f595ed5b1682e8c037d3cbf605b419f2a193907b3f538529add5df3160b281aeaa07370682f7a0d77e322f0c8ea42f2b9c728fe14611c43b785141da25c2b28755215f7ccc05c4249d766da0eae3a9211e92c56f4ee5ce47d7e5613830a56efb3d544cfee8c1305ea0352c0b3097b55d83a58c2100f3f9e362a482ec26c55c9bd7847e60b350418b0581dde1c387213032b773d1c741dbe990c72c06ee963fa77d33024aa1144924aa03c198da74e46abad741f8bc474813837334e91d8216a72f57077c12e4121776f7b1390bd3cc1f50a06683fe6356fd69129c235b444078e2615ac3cb51d49c21753986c7844b14a8c2619294b1f88f08b299bd1d639220daf410f0db279815d19376e2718711c69c94f07d1774df92adf673bcbed71e4bc4450eaff3b4af5c32956a81ddd449f9d6a682586884e4bafba1cd1816a418a7a864fea693b41f867d16185286c639e34a1378ed2fc7ed687a3677c7bf030952c4f717bfb3f39acfdf95bc837ed479b249038dbefc005dfa863161312976e029fa32c2021b778795a486a6aac8940add37126295a432d257abd180562f20dfbe0466e548e7053e1138262dcb74625c907ff781c3ae667d2551e54c4d67068fe903b3fa242053fd3dc1b004edc7474827e7d155974b94c8b6b386dee8c1d0e90de8771660cdf6981deab23e8010975bd5b0813b6b6e306fb425344c9b70938384bff772eaa964d00c14309d6b1de7e113da57a78dbf912fa5680420290a249a2d92728ee8fe549def1592fc3d4320a3b338f3ea1607e05210846639c758e2a1becdb2b2d51231f41e61a28eaea4320481bf963e0661e01d252e0521acbc56956fb197d2f9a2f44b7d4bc0f2ff62c0fa8b6237036bfc02e8621d66752bb2b694a63e05fc82e482cc1e02a396b6c8015a012b973b251220d971705503ec73638a4289d07fe3abf21402bc00994ecb53260d3321060b2980e51973576d2f0566ed188803a9005746c9c60a3502348be3cc69b763e2758e30c4644fc1fd7b06f4db41235716be8aa1188943662139da00c9e74b54e6bc6e45b9544f44875116043ba661634237ea551b87be70938412464815c2a29f02d5507df468a671ea99e6c83f44d5af5cec9320006bc01bfe5fa55c57fc13f63f2c76f2fc5743bc4a1bd20e490de1e40b4f80c86c5c419f30ed82c315e6f1ae03b541bffda0e664003571d6c7197142f89b85706e07016a6c4f54085a42220acc081165ff4467cc9d9fb00db24a56c690ac64c8c9cf41d219a406eb6b1ae776920ab2970e8fe7a9984de7bc622705d2af9cd116a7bc96d89948e056e0429001fb4a9247117602075c4045f119ef9690901f7259b6309030e357f0defb45719afec260a8218ae6a5c3bdd3724935f0ac6b0ed134e4b9a49450fe572261720297ee092347cc0662c96bef47defd66a7dac37881dda60517b4f892e5e6c99ae70f2cf2f144cd8f638badded6c19d2b30da18ce65b0ed39c69b7f5287c2d68084a1f6601710ff33d2baecc3e120d19d6210a524018f7508e65feceb26d455a2c05c79ced5e6cf682509e13f66014aea4240d792c1194b52977532aaf35cb435d4b7471220737a12a7188f67b50fea8f17e91d4025a42af0e4092dcf425dd60313f3a275a5d6f02786080aca220a48fa53de7d2845f43c98c56d4550c79596b3d7cf1c21f53fcbb0672ffd8eb585d1a9c512dfd7f3b08a49d5ad7168b1dd7112c1ae942976269b9ac4f9fb1ea25fae15a7d3aa5fc6a5aaf374608705f24c88b9e1c2358da23be92b31e084fe05215677933765cdc7cda6cf40ae870780d1fd8dd50f9e9ea1d8fc1ca09eb49124305af2d270655585f40dc800490b9736e2976432e3d38c231f0e3c276725157403ed780192015083b90746e59661409696a261f1addc05b3f53b9a205a0334c51dd52b84f407b8a489ed39e28fc267b358a23ab57db938800113ab76249052a60231e69547c90af5fb8c1797106f38e4f6087c475239f4a0cff5acc10a0f62843d111140a26c1b443dbc1937aad1f8b21a40ef7149b5e8341282aed43e5f9e1355eddaa637258c27953727b5d29ecd52e70b0e40e0a706d5b2ec63f0457e97162a6c1604379813b6d84d7db03f8827b025efcc557864d04739fc2bd28794d3e2f8761d4793539de06fa479154be36d120387e2945c68aa4106f0cfb3a7fc30265957b2d70cb3dd102b72da842a05bf17d51b7d735eec14d5487848a6f754e653b9bf94d5624109645c27a451864981f74e3592600413305338964586052e12f4987da983718a4dc04f8799c261da8a51e329fe253c61aca0b4836e83c2805d227a64d30639105da205b1bbd602ce6966f15a2f57d75b63c05fbd5f95bb6398a12df70f0313b63702a9da3622e48e0514dc9c46f1ca0cda65de957e23c85eda26683fd825d0a7b5c683498e45135cd1419ca282856f1a1124bb95a601fd063be0d6930fb014bc982792607372adb87201f84ce293165bd853f9dc9622a8d75070dc90d8a297a93eb385b218e2fb081964021e37a7618b388444b6cbe0b1f009f18c6d0b47d71165331d31ffe24d4d10626bd24d473ca591366c2a25f778a80537a153af425ca34c4485d3b302ca778de140a2c51305b52321ba8680847c6b99f4f0edd1c6266dc34716c771013b628f7422fcbab15c3725843ea7c48770aee5747cc65660436869e3374f2f34a939bfd07531b69191acaa4550fcd4011bab5a2525f1d531a56411e040c0ecf5c2070092b55989d2239a950129df4da59bf96f23ac3407d7ec73cd20e47988c78ab3c6c7d58730b28219d82569dce220d57067d5adef054182d8e8e123c9b1514b531f26da4c174342491dc19f054d028399929634fc01406e4cf9a6f652dfb51ae7a6464c118614a33456e5a9bc364078d61132f6911d82a084e026e2254fc0b0077b73a27335d6e5f571b55555ee53fc1fd90514d97e25b8138b105e68371442d9af2576e47d057229b427e42504b3540143432bb9c6b7eb2e87724d37aba450f99746833644467ac55b724f2e54732c792fc4f28268f04cf2c516556f4a51f392994627786bd15671e512ca7c9fb1d1b42e030c255200de2a2c90e2cdc931776a70562fbcd224b7a805c3ebb1c9d21a73bca535dcbbe404398ea7c86973270c91cfc2e825e1463a18583711337db3c142f304b83cffa5e6637c13f1f9ebe24c9aed30aa36ac31be7a0194255bb0f6252450f3efa6996007f1fd650ee71da2654a1b53e1c65e72a022d2f5703d5f91d52f3ed160a96242263ca19784f24492876a52d2773b2571107d4a57c92f7f17461014c0f40c57264135f954e14aee14d53815333f2f97d0bdd385d78f09eb14e5516384507431a1867c81a7075881b40ba7113676372fd7dd31a230720e2d41e63fb4e6950d6282f5a46485a7cbf6f4428001f06fb505021bb611f148d8cff5f08437376dd82a55a7c134973725a945e39ae750f1b340a75fdace4689e13c11488369d0c33a53c397ef8d52dde26cc19f57d845e943f6e12a64ffa5ddea6785474bcf33935123d5d095cae5f7e35932a17866e2608886c5e299b2c542e296849d0f1db42f77a5c13638a380376512a568069081512236a5a9bfe0b25648ac44d78f8066c8e393e09ae17af3122a8a433452bf73ac0c69a43541f8a104323a310ff31815c835e702f19991c502060ba26efbc1060d6e59c488ab31f06ba13587cdcac245c58020e19013bf16be3a60072cbe0781c5739f9649234843e96f321124c26eb795bbe7248e192e56f89af3401d3961e43d2f4c4047047743a677cde3f8d62d20dcbc4786d8cbb84192bcba344c81d5002e0d135255a73d41a21a72a3c4286ee1f4890553981de4b1f6d86161c40bc2b7acc63163e1af5ba31b38c92450243f052fce15262b9e1ad3074e05f4e79a1c97211f1d14cd3ecc8297a3a6115f5739979b58e811b27d3482749db6e7b600832651623e83b67a4ee6317440f2100c4df3c3e1e2f3e4bda7c2f7ec638018b253939c225b525f0657836bf7af071a50b0b7cf5010e47c6272c0e4604964910d0a34c9c5c7774cd19b82da9a13d22b0f4cc5a63fe1e41d4e38d6f7aa25d12f113e74e7cfcd028733e4f1fbbce14488538ca1fc2151a05c23b4356d4e6695cd6b8b4324c094505874119227b6cd91fe035150c0452aa0760b0f4233a9d785b9eb1e43a7ac1144b9ee681533aee563eac7cab720cc34672bad53535795bef59fdbe82607b1a0d2c0fb70f4c4fbee44cd1d4cb4b1dcb65176dcff42b7b7fbc49c17d1f698ecec7749744057e4a3597390e6c3f4f666a86104f6b1a2ff2da8606ce827144ae36d35465c5dc26a424701fc3624060a676221dc61f1279ef978b47235c1d0eb259c8758d495445c483e4543dee5947e3e8ca6b02893759870e094195798627f23fda6e4ccfa92df46a2c0b847ef13bef974d7ea4adf53ac3827c3843c9c3383e561e0b33e724620dccca55658d8b54e291695c2e2abd6c7358f37d6b7e52607c40515ea9d2aa3a61048d59b31df94a140c7803b871a75ff9529b2e5e5bd32de93d4048b61c4d56c79f78041de38a79f4af57417b2eff0a4a0db843890144319ff71e3fc9b84d6856b3fc1361a9471b915b2710b4b83b353029fe3ea195971ed824f92cd553a025c4098f3af37cfa7af738580802f702770d22dd63548a6770becdef0abb9ee02aa4da1806dc3245594f98f569f0ac63630f18671652882960c5fd82711984611ce0a9c4525ef7ee55158bb2553b535676c9808b7e4d1bb2223c4154034d1dae24fa00d63023d182331fe9515047a79b70694a4e1587bcfa2623699c046cf7b103b6d77266aa04eb4fc07ea255e3ab966ea7e9985e2e51120ea39f422eb979fc2789ae1c3eafbee154215d3b558d9ed774173d6e09b5057f3f44e06d49244b03518da35d4e9d0512327a798d1b927ea006fd0ba6555f42ee69a87ad00cb0afd87c2dea404a28eced02c25ec27a79e72808607f5022d154e970f3be05635f98e10b5859b62770aef0040f8d3953e3fa5361be78a31a50c98362ec14c966452ab82789d35c421223773408f85d27622b373844caf639c2972963b9076a10af82b75759e15e00f2a8da678a5b88376075833dac88b21e81621a73475ba536719198626bbf6b12b9a0465b83c9d5311e70fd73c35aa54a78801647d8774c34dbe9536db6734337e91e9e4ea0dc957735b43c4561ab1612c78a997288b90d3e3b3f400d17978450a7f20307c3edef006210874d2213f35e0e114f5f320481760b19ae7e8872f03ae7cd674867a9e11ec9d480551db4c554e1a2fa08335925757b5d161f95086f0cd628f714fc4f4c57f2930a3939f2716bf98a9d6585d9244fc436fa1da52ae01d9d77015175bc641d7116dc52c337a13d8ddc6f3f66174a0c335fcb1344ae0b1d293efa3c2b0ffe3c363d0024f3142a741a2fef23dac92e1a5590e55430cd5d6cb835bd3ca96faf7ef2ec1c06767c4e083deae30b5e96c0171336203f13c4d10058c1c95c4391306df980680f18d47079b7dbc01ecae07f2ba2bf9720f4c40a656da7c374f754894abb257c6f2908606019a2fc65bb251c61da85f454bf5f8e231db7307ebdbea03f64a90022612ee2575718de531e9c877c5656fa01e57e8f2e5af5c77990fd9b39910248199d883908de7d2f1b55b92d264dd893736dfc76223c4a6657fc99302371799524fc9f6d1834c887767480596ff276ab5d74880725c9c387768dc2a33056928f0e50cf88213fdffc75dbd35b53db8b6341c45e0d4771799321cfb96c3d9d72ab2ff8bbbb669d672a386c89a54081cf7e26015791240498752ce8875e40c6141f264f17a653028f962091eec14839d9d77e798f5313aa2c2121ae869f725ecbcc47b2482f23d9446a3e64f6933fc0313b2a65f53d640395285be771d224053edc4dd796077c0e79ec7d62c9f30848fca230e9a810743bee060ce7e4e13ccfd3ae484cdde23faf778317ad88843a738fc634c791fb2cf6dcb367798b0a5fd2cfd5604403da5457ce096d3af4593ef70bac5908b7663d851bef7ee06b7042a7b1b630965062332e59762bac095770c2a3367b25d0715438231e58e1a0ba258db5a30534e764077043cc3ccf8f7b6b325d287c3033ac17ab50c36416f0587b37970e6f3af35564827c2d779ecdd108318bb062a15db159f2f34e534288595527f4641a3d12465774f31a6bb8a1045f4835a9595fe5b4121bfd18731685e37a162a4f22a663bf4f0f59e51377c5b2123638fb3c3364744756025e01075bda36d4108f23bdfc3c52fca0647489fe607e870cec4d29aca3216d83474f9625cc727464c10018fcb5079d4731339ee1515c2ac4cf57448eff28037a72216abf05738b08f42b267e8b360e7072312a01901223e2eb14320bb96b6910744925449f7e4f354330672c512a75ecae2af427561210f64972450b45297c0347621e12632a3be0956789489458ebe540773dd56c4fcea5f41bf9be8875e6bfb56378f8cb47a884d24926a7ff4930c6084d9bb94d67a23a5e4f192ef225df60367ce4631850e0cbb72e8ca15d0789f42d17e1d0ec5860d3775b305886486fad9348736bdb439e0dd61566bd26283f36363d88ba3064f4d13551a121375647f4aa62700e712d5bcc9f09691b5e56b7e83c28b0e3bf3a2ef2b022b161d269a5511a108ba54047d7343109552c906cbd98760319a5e5074a0b6928a967641f5509627e1aca7733f751df135a4453086160f539fb2264678bac78542ee9191536c0575f02353d7e8d5d2d08e5aebf0ff92c2f19ec490957a235c04ea4e4380b22ca4c5a38b8064abd523e0277c64c66e93dbb6527b5072d2ba77a2f84b016448cfeb409074fcb2e7000fd09aabc0e4e2ca6f374ee328d36b399f9749539f6433e5ac268622a9c418e9f684c43df0b162c90ec6faa3cb81ef5ab856abc2ce803ab4ba039b0213802bc097a4bbe05043d578b026ccda40d4cde5cc2741f59730758280f057e786c3b940e705bd4b7d65be1075155938bd7244d5f7220c177467910718d01bdf1d96e30728b6e54e3571b9545bd19ce23e55f4270dc78d12b352750207f4c660d4b5600bc372eb26fdd326f762a7280585939585a484b0ff2f34f081cd6207ed5c91b8918c814ed94570884247f3cd2e91677b0564376c5f5e5748431745aaf97e838697d5039dfaec513eb8db05a84f4c80fb4332913c848a04b11d21d2ff5942f2385419749f6d273405d91bd2863c0b97e7f28f85f7b894a1b1356ca715e8f29050a33fd2556ee6c2f4ce4f6166fedc60129e9467572fbaa1e9fda5d39e40c31110c3ac5532668034fadff6157c95d491c03ea6f03e662a20b8792741d2d887c2483759e17c54cc4569814e802271ee25ae5d84069e295ac7e3657452653dade1f24451109b3dd3a56deff596fc9af8826bd1845331edbca7daac2cd4a42840a06c8c013565cc5ee1a6484ea4e150391707221db1342682c3ee27f712a223470744ec4765a43e1027e346215430301666467df17130637a27b989a5154b0f73867a2b4917441a7745de24dd441dbc8137067137b7bc1ec4917411b5b257b4f77019f430f59c9b823646501bd4d94ed3e0a1380ba053c95440c90429a0b3567e62b13c6b86a2b27515006ff8c2b7c66a52b777eb360e525ec7411cedf1f58f77e3d8467f03a699db0311636ca532c82961a703d545da8e5733d752aa36c519fee0a54d2820ff9ee94067fd329181ec1b06956855359a4ae604a8424891c4b44a247612f0b4ac7677d15a0fd7739c9ebe07334790305eb8915601cc07e6c71688c4cc369fb0b264f4f0b5b4e046fed1aed04433ed7535d3c6347e045e6734a4a6a31cebd2a75c8a7f770ac1fc17cb399f75c6028ff062f500838fedd427e25f0b77a9252926554856f5eb132000e81994944b59323497cdcb55323b3bd7a67668c5df77f4963bff516345066e0639be1ad2e1edfd61fbf168548d20e717c388e31431f63156e3a2e2e5f684c0a42f6474441c9c94a5beda758703aa208053145932e5e21061dbd13236b89889a7d9cc188320659376ad167747ed8e239009c7756343e72d13e0bc56a6847fe73554b0de87c31d8d70e96f9165e7f9c7a583630d87b11fd6028fa29f27212ed4d18792a2b392929235ab2e2944715249b7df0be4d5e3cb4e540a0a62249f202950715249b7df0be4d5e3cb4e540a0a62249350f68499fcab759776e6e09e5db277b0d9148490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466ca4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466ca4dc9a2c60e42529fbf8531a755f6b3b36d0966c92cce835552a4c57e45569335d998a39fcf63822f2383523b501862365510d229133283a7a464b448f99ad71264cb81107e09776c0f6b142a57ae726e0362424e05f0c1ae3b09b355828ef49a7bf466c3dfedc453d16e14725e4cf64569a502e0fa8421521b2556a086ff867aef8b019b411d02ae76b9d30ed745221dd9f5b46d9f1ca4dee6efa520802db5e4b9ba13e78a6c270a77385647e18eb31d57737632205fa42b6bf6703769e1407f4683e39e35b2f15f6d4d62ef69d631446f35f1fa3f3583feb45630555a9b700137a7533caa4e757abbc4a64d1bab05dbcec1159fd0c337d45f2ee1af52a976527016d3e65275f6de5568c12b01aee07eef89522998f3635aad95c50f0606a73d9cb7a0025a6f42864748b053c7f3043b3c2fd4b91035c4b1d62c82db04d9469a3bbf361f817a2038838f80f6e17a7776e7dba622734932bb5dac759c45f83230bbf5c6a926a827b5a7d8515e640816a22ee7b296922f760b55041663b4a494c3245b16f9f19772768cbcd1cd3377f39e6baff3999a23b0fa0e6e046fdc6025adb214364c7632f4570807a144121a8769ae8fe1711e6532e8e50ce4441f9122d507c283c0013053b28b80945c838b55b07d6b0268ff3420daee7f13ebe27d40db232ff337b1f1c57aa3f03456a16250fc2a35d4ae93b6260675c0b2c23987576cf975660671715603ec19a54a5907c7e16e0cf6a0b5a3c1fe453aa381b72b9701154ef46966c510748307f1eb9e9534e3a6647363d513b112181a84b3bd99b0966788f1c33c65d0b4b3cb904f42c911ff4ffff4bda71a53329ef4c0818008c5e5b417f1ee5fa717bdef15159a532b16d000000005819bf5d0875f9098f397672a51c773e8ba8f523e9755d2b4d51a25500000000a9a6bd5e75f3585bba7d015417c7ac746f90de7a000000000000000000000000b5252a0a5dcb911d18de6f3a1c2d531de754da7d00000000000000000000000096215622a5f134011d8ceb5062e0700f547a716d000000000000000000000000b94e21612bcce355043d183526174a055bb3a610000000000000000000000000e31cb80aea30587b508ed85bd3177f39452e4a570000000000000000000000006347801c32991335df25192a1c8b125fb43d4f41000000000000000000000000539fc537ff6c887dd5ec6b58e1d4c77e3cb97227000000000000000000000000e022631ee43c3e18f622614b48887d194c86855c000000000000000000000000a005f653b2ff693ebdbe49022b09473427b7d559ae8173406b5b690bac1630403b9a3b068bcb2404247f67065fbd765bfd1075407698f35c5683f73b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa214e5c8159f75f02de7d0174f9bd350431367d33a6900f33b3796700000000220cfe36a4cbed5690c46b7214a2307d18c96048222f7419d148872200000000c9c7b439d54854699756167c4934e3208c6c15390000000000000000000000008aecdf27d32729696f6625032bd5b310dd097d04000000000000000000000000af65bd01be9a717e9dd66a7c3e462840bc0b6c1400000000000000000000000026448a789ddadf441157822dd75d835ab2c8de48000000000000000000000000fe3c3a36ff232f5f891ff417ac51995a503c4339000000000000000000000000cfe0e426deda0f6d94d48b6720189e3babdeb11a000000000000000000000000ec54c04459c46863d7db7b455624d64057ea5a6e0000000000000000000000006607d8106403782d7eac757d77d7235c038b5504000000000000000000000000023be52771763825088c202e382c3622528f6e38282c5150402d8f4bd5bd4f3b14175256ff90d60c0110ae26921e0103a7cd1f797bb28543fe2c777d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7daff24e0b28f430b483e12c0c3d551e94b672b9a573263f46eb35300000000cf093819e0e8a8063c6d675e0e61542a1448e24744763e53d757a31b0000000095a1e66a38a2ca3be2bcea26b030034a8ebf68390000000000000000000000004c2bb6666edf731165cc2c5d4577e43816a27677000000000000000000000000e45fb6499544883245bb2e0d124edd5334394f4600000000000000000000000049d3866245f5b31807767c3f9664a06ace6ae65600000000000000000000000077756208a926703bc3bfe93ccb442f31248201600000000000000000000000003dc1c2033fcfad66d19a06052980f34b55ed796c0000000000000000000000008b401e24aac0fa122a7b7f45b1f73b09ee2c2641000000000000000000000000df817a08573cd66defcf5900071fa40902336b5100000000000000000000000059e12b69d85af0209f675c507e538e12a952a857667e3f39c695dd7ca54fc566fce5cc1eaa7ca955f1ded20fb3264e4d195e8e5dbade6d094304752e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083419c321a47e8753673923ea50666301a61af607310850c4bca230b0000000076b2643babe3083ee0092f08de35f856f509d03adc1cdf5a45c8323900000000c3a910323624e416180be54f37cf284f742d936c000000000000000000000000aeb9c85deb934a28f03a0d67d5db692066aa600c0000000000000000000000009946ff033e2500220c1ebd25528c8532022f3d05000000000000000000000000e975227e229ee306d1d2b34d4f173c0a152369300000000000000000000000003b56ca449d78ac0d626260652f8cd62f82b4d7350000000000000000000000007aa78e0cd7f6e46aa063c974b931f3418d079f430000000000000000000000007823f74a3f594b13d0f1b96073951540f8604539000000000000000000000000a3a6272f3210e333544c836edf06f76ed40fb6300000000000000000000000005c3c642b9600c1706ed6033bda20ff622f295b67be9c7a61fbfab94100e1dc5cb2bbfe365bc85f33c511717d741a817023d0aa3df7ef803ed3798f090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006789c35a532be5459f20a82b40f3e72ddb1ee229ab4aa570ff72c41d00000000c8bb252123f42d720d474a4ae8665a6bc6fee01f3de72f47e7d82c7d000000006b8c9e47e9ef4802d4993212cf7a197b02443367000000000000000000000000d762f13daced2e6345147c7823f3db1a18fd3a0000000000000000000000000058159d582ce4930d26b6c03bd78ced509e38f5710000000000000000000000007c91f0756162e4141497570b8fbce42e33ecd10900000000000000000000000036386f0e45a2e705c76eee754f0d1414b3d1fc3e000000000000000000000000d350334c0cd6a84a8a1e3810b36fb2007b67903f0000000000000000000000007bbce82ca471215fd16a167aa37597344a744863000000000000000000000000dcdc6965c60ba323a2a7cb0aa9b6ba5e8aabfc64000000000000000000000000c3431a06a6243c51c6a979638512951ef70a4165092b8d6531369c3b55219c18f1276d333a1e7773889f6719521ea464eb81ca75d186d84654ab830f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033f1d78c63ea771729fb7434a1d701bdc1e9228d8fa572bc57dc257000000003bbd394d7d56b52214667855dffdd26c576c0855c0ae5e0ef0d8581900000000dbd95229b2a59b2450d2a87c31c93d7c0f0ce045000000000000000000000000392d44226683a82205a9d1095c40c619b81f8d62000000000000000000000000ec38b7300500613944b70d3c965c6613f114ee34000000000000000000000000337db47ceba0120fb0ec477366a1514d4909af3f00000000000000000000000052a53e1566b2af0ae89047572a8fa0548a29585c00000000000000000000000007ec7f407915632dd1a66537c4673410a511b7420000000000000000000000002d0ad52dcabf45272c51453243866871c1f9e619000000000000000000000000f079eb42e17cde642bc2c519e7b1db3f546bc4010000000000000000000000000a70b22ca63d47632f5c517c233c0b228addc610879c065b95b0b97e9a81ac3be87e0c695cb9144e55fbd756f70e40748f436d02dbebb76fa8e7602d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405d8f3bc2ea146629b14d154a4978734c5459269627894bd6c7260400000000b847cd17c14e9f1af6899562c114953f6bc3185e0ee6ba6b225e647a000000002219621ac1b2e00d575408077d5549506d539501000000000000000000000000ddf2494641a5e5314e03625d57f0a5127888e8610000000000000000000000008b0e264cf4439e17bcb9810480901b516a15516c00000000000000000000000006399313c956f85a3d5dd31d37429b744453ce6f0000000000000000000000006be0e6627204581209807362f10ca652ff04ac3500000000000000000000000036890950c1596e743ed5935f50958d3a6a46110b000000000000000000000000aa47301ca5b2ea5b1d11b1507714a50c15cbe615000000000000000000000000b51a486ac4110761c86cb96cca0c0d310a03f35c000000000000000000000000327fb076c206c128590d9071639cf10b80ef8f2d4f131c267a66941058611e6cd3610d0ba1e04a74cf644872e5b10f3037cf244cedea850667a2f67d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c742132007dc8776b7c17a4fbfd9542dffdd9e3096b77b52442eb44c00000000db85d636ee4094335ec16541fdceaa587fa22a2d8ce0782ae5a3733100000000be85dd4954793e593e7ad32ff33dce01b73ed4010000000000000000000000001b4bd56f839ef36a7bddef74bef6f47450bae65b0000000000000000000000008739965b12ab1c534248974ca0beb8057fad54030000000000000000000000002290615a5e11595d29372966811a9c1d2a2b3c210000000000000000000000000b2e381e88054010f0e0333983ebd12eee1e7e63000000000000000000000000c3d7975ca2e160581e4f8a4a8dc1b74058ca6e3e000000000000000000000000625c2456f71f5c02ca577b43f8ba2050fa4b343d0000000000000000000000007456d145c7add51cc7aec2140bc8b9166cbd08030000000000000000000000004fdc112f588783726e5f5a4a1a59977292881307f12f004d86042c595b695916325f4625b996bf169551b72304d59b2445dc3922f6c0096e8ffd1d39000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa4c13321ce5e374f3726a27eb9c5e7476343d768584492759585e1100000000e3de212c2c725b328d1c001aff7f65721cacc62ef0cb2532aa2b5c6e00000000ac403a53cf9aac11e8dcba1924d93e551faecf51000000000000000000000000b162312f8635de491a19b542b4bd707dfb3d152900000000000000000000000083faf860ce321901b6ea4237cde3d06e9f559746000000000000000000000000d9b3f063795d970d2342837d205e8733625550730000000000000000000000006a497f3701d1296a584e4935eebe8d268205d55a000000000000000000000000b04c9b6f34298b32b808f2144420750d1908464a000000000000000000000000ca3b0d37f03a2a1b8c551e7a8edbe55522b394630000000000000000000000006079f00888dac336c0009369964eee37fc7f7c7b0000000000000000000000009542f94cbda0782646006e5ba34a366565ebb16af22b1a157186b67d16fd803f05efa54ef4d36b67c3a51435051e1c2a3edf1524b7ab8e2a244e5b07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba6419623ff6582b7153d85f2c113f2717526b505c753c6a357da665000000001c9f861ec9d6ee2296ccee0c2113fb29c8dd9a3e523c0b5df54a4e70000000002979152155f954121ca3b73071e6dc7356add74e000000000000000000000000ee22da2e3f155c4dfa297958ea9a07320b3c617c0000000000000000000000007618b048a6779c407e19b17b6f3b941c5e0645450000000000000000000000003253ec6e8544ba7954bd1f50f5ad1e5bbf79bb7d0000000000000000000000000cb6a25e10f18252e7e2bb4919486a418fb3b4510000000000000000000000006a3e8d65edfa3e64fc82d86fc466487283a1f07e00000000000000000000000017a7d1658791952a2b770965fa1dbe3cad66e739000000000000000000000000ccf161093ee5217454374a285b66860bb7ff4a240000000000000000000000006beca623d93ff61879a86320ca39947bbd31d0099943a61abf2935439b073451fb08c85e8bd1223ec80ef36d200caf01f1348906372ea22f298966300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a9a415391bf613d9621c1227454862a6a69a9172b1b5c4de0337e75000000002ef43c479e0d3c08b7b9c351ab24a2582fd3ca57049d27403ebafb05000000001885b246d679820b93acd20210477a31d56f110b00000000000000000000000054994d503de96422fe653519ebdc97368a83120d0000000000000000000000001817a24f50ec800069ea76317e7ffd282fc46336000000000000000000000000b143c96da2ced04a6b75740bc9ee3f1047e8a20600000000000000000000000072d292318cae6b6de4dd554702859732c964e1710000000000000000000000005a132c361856c439661100791294856c11857e67000000000000000000000000dab2ab369e77183a13d8df1c5467fd17217f7144000000000000000000000000031c3d6ef670f11d2f39500de7d0466fd72948510000000000000000000000009b7580259c902735e7d4e70f22d78f01c768306b4d0f451f2f4d471fb6b0a41dcc857d7d2a5a6f5902eb9b72cc1625497545521054664c464350156a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b07b3e5aea585f127fd5aa2ac100aa08bd143b39abe4f94ec9f0143f000000009823507e6c5f137019110c7a271bf434277deb52881c095f023c614d00000000bd1c507a769eeb0f6f43622a4b79113202da2c17000000000000000000000000731ab534522cfd0a22242045d836295d4f30730000000000000000000000000006e3c778ad20803f3c1357107aa36429e62dc15f00000000000000000000000083438323fe7dc43c52240c42dab0924830e482610000000000000000000000004873f01ed8ca2b69afa64a289341eb76fcbd2a7600000000000000000000000085817729002a352c77c979592b511713a938c849000000000000000000000000dc0b97266252f74a23358c260122840dab80433d000000000000000000000000688a92139081ef361fdb6372f9746d51f5472d07000000000000000000000000d62b5a1545ee0413d6ef6306804773322420811dd855514b58cd0256701a4e074896c113bf29582406264362dcdfa26f1bcd501bc6790860bb63611d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a764f798688da6fd54b950457ef5e760f439072b1e35a63e8bfcc08000000008ce21f645d8b5d760451d94f1977c22f2c73cd70b82de46b5957a6490000000072cb1b512a69440ad47f26276735f9565d3e141b00000000000000000000000064a3a867eeee157a3583fb041b1e36311ee26d2b0000000000000000000000009b0c611b81031f7cb4b52c3e451f300ed5691551000000000000000000000000bf1a1560c8546b08f17927619d9d4a0bfb75e10a0000000000000000000000001e33db3ef42c3439e028ae2c4688b3708731be010000000000000000000000004ce0dd477274ea72cb7a57644fcc326efa35e96f000000000000000000000000c8f3717964eb0336e2f26c4048548c65d168017d00000000000000000000000061b47640dafbf31f19ec75366a4a994c4109f115000000000000000000000000aa3630391f014624dc697e2e8f5d6d53fa64770d17eab7054cf3e627e0344c512b97431de995d4769073805ffe072e2a9d20587e24a3d60f405dd3720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4e60398d02bb31ca553d160adb4155828f315876188511fcefc05c0000000039abdb5ffa559b56d27aa476612e822c221d671fe5074745595b241700000000bd1e0a46a406531716e0fb676cb5f0407e925a09000000000000000000000000691fc801ef2b965fb9680357a003a24b6b88cf49000000000000000000000000f50d4c264a4963622bb0646db5c4673ffcb29119000000000000000000000000dee36e637c9135448d31b36253c0b0309a3fd641000000000000000000000000bf314d3db21e4f412cf440222042654ae08dd30f000000000000000000000000fc0ba10b63c74c44f1df621545a82b2b4339cf3b00000000000000000000000028dcb6099e57fa74e9272454eaf9b173fcf862510000000000000000000000006682533b2ac1e03f4fba1a157752ce4fff9db740000000000000000000000000220d59594cddf612f67ff66ff93eda3c939dc07800cc3f612b31f474279afa1456a4776e446c2b57d503766991d26751a7bbbb56b52b2b030e3b987100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032d85473c0c8ad27bef7187bdd72741537dde823ef72a42dd189c930000000004523e17cb1bd453313a22e0aadb062382a850449e26f781e18b94b7e00000000f1d6336a39e5255c72cbb56612d2b71cef290c5c00000000000000000000000026084c216b137d2193842862879c6015ba94df62000000000000000000000000c9f0a876bce50832e3a2ab26a4694b03439c97400000000000000000000000007ae77a06b1078872c8e7b3767980474eb13dfd0a0000000000000000000000001c0f674119ddc30fb17b307cae80e73aa54c423e00000000000000000000000011d2f47690dbc931a184207071d49b49b62d021100000000000000000000000053937c04409d0701c8a8ae16fcc7fe1665ced67a000000000000000000000000c324206e4973a00f95f4f33dead8762eaf75d824000000000000000000000000e5bdde35e43a0016d1bfcd59ab89ee4b551fa779f2895e546b4cf67c864fdc5c8e078b6ad174e23097d51161eb0da91897399c1b1c55fd450459df3e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8073938a6d4c630ea02974cee38b32a5c2eda3470119e6478c93b3500000000770d944c2b7428708d4dcb5d8a04976188ba9d5fa4106a0ffdc2f022000000007caf937b60c8c9620a43ab444847a079642740530000000000000000000000003262897c1627bd7807735e4560196f34e9f094740000000000000000000000004f34c319def40829ca70442b9ec3422b2219013e000000000000000000000000ee25d93be9e47d657699a40e7a319b6c8ab46f0d0000000000000000000000005cbb8f67617e0d2810a67e3553bf0617d85fad170000000000000000000000007055c04526bc12797cc0f01dc08d7d0dbdeb694900000000000000000000000081166956ca10cb6d83991d0569d96f0a06799827000000000000000000000000ddac8d46580d7b418a8d8403525f3a59d9935213000000000000000000000000ad0de767eeb4653616c4692a92fad27e5ab287553d645d3ea9c4171a214e92219c597f2b6470a733cceb0569606f4069924dac2f8500d338cae6f4020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008411e3465ed487132a09617ef1efba2f27638f5093b43d0746bd4178000000005e9417395f592a52bbee034520631f2b97f43462aff1480aed860c780000000074684673df79da043c2f003b14938d0a59fc87280000000000000000000000001ac3395a78e56b6fe6dffb61519b8b06b5406d1900000000000000000000000083d39427033b3659c10e4101cde2692577615c2d0000000000000000000000008004aa0d0f858d38726f197d0df4ac1b7634af5b0000000000000000000000003e4c801278c71e77ec0e2c5b296baa0c46752f2a000000000000000000000000aa7e9b08741a160ed3caab2e5060512877ab550f0000000000000000000000001b3fb961c946b709218f4045bd0f1c48aff801680000000000000000000000003cbc0a23cd88000ad9fefe36c148965106de8b2300000000000000000000000061bc5711a84b5e273f9a1576bc342e3f4f7a5b24055ef46923628f04114cef6d5532762a8b7b5f2af06a6817890dd04a361bf0519b164c269221d72800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002740870f667c23588c59a172807365c0a7eb22264e40c14ddf0202b000000004a4f1277a70d8d718486806fd82efb69d45719191a585c0cecc7f173000000007d7a10661a261220d74e322109f76162751b6327000000000000000000000000441e633ec0fdca6e177b2f3a0c75d06f54d6603300000000000000000000000047a1764d61ee1e35a410aa2b22480670c606f345000000000000000000000000a8c7d947e4e49b25be045822fe35b6785b2816690000000000000000000000009cb47c74b6c32350de0e38000c47720361916746000000000000000000000000f768aa020f52ca0639082f724d334e52304f9f28000000000000000000000000dcb29d73c7724127e4bcfc625275ac597d945b1b000000000000000000000000da47893865233f0ef87ccf54e775592cd5bd6d11000000000000000000000000b2297054cd16780f3e5a33200c2dad3b3657003576274e41a5191c172ea5643f27145e716252c400cf6f156f8a8bc460f9c4f1618c45d740bc0b8977000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa4a387617a7fb7e9d94b92ad33c971de88f8e13c0ddd3156ccfc04900000000ec0ca34d2061eb4064a7501e206c425aabce7a3ee6a764324688062100000000fc9cfb3b0de7bd29a756691516d7663f4b5bbd4f000000000000000000000000a1d9d65b7a7c602016f8ec75c2fb317702654e73000000000000000000000000bea75171f3a0bc28a5e5394034e9a91a2952b80c00000000000000000000000001934d08c2cd100a312a7243862d164cb4db362300000000000000000000000090cb805b33d2302ea0bda86edd39b346cc701054000000000000000000000000f887923e1aa65d5e7dd7f533a013102167ea7c46000000000000000000000000e7f37424b671db106330ca5d2b2ef17a38c51b360000000000000000000000008e351d3aff970e14a47cdc6edf755b58ddfb363b0000000000000000000000000238064afd41b512f65c96149f83c0316919d6726678df0404543040aff01c57a7c011594177362c7992942500f55f317083321df4f14560ebd3e05e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e3b25c794bcc9721e18c4854d9cbed2897f04c7dda25d52c13a8cc0e000000005263f56be50cd65614be235112b76b5e04886512a9acd0123ee1db730000000023ec6f168589dc129477916503298103f50915040000000000000000000000005e63ee5713f67671c4cce859d1f61228926866710000000000000000000000007c4457069d2fae101b0ad96257f6f10f087dff68000000000000000000000000ad23c20b01a0e3759e616807765f427aab624959000000000000000000000000bdfc9d23ecf87d506278986b3b45f5607588b7060000000000000000000000002280c11345ca6e266102995018e7a25ef984c568000000000000000000000000ebdd826e66d9816305f02148369bd76baa20f82e0000000000000000000000002e41c767b5c48a2c4bde3f12fa893c01440ac95c00000000000000000000000044641c20f5e543087af2b07e5be7af08f4a6bc77983d6663c5423922b0ce416cc199e27a17bd8f70c346c4218959c47dbb83b35dcf34a2523c0945510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d57665bb45ea6152a9ab718fefdc01a7b56665c0b65004b66b3af4b000000002bbb006e75122779aa732e7e52a99d3b1ce4ce702008892dd8f09e4c00000000e16b156ffdc6083c0e9e6f4ac58b54787e5dc16c00000000000000000000000050084e2d8942997626416f70dc2664089dd5e370000000000000000000000000917a40788410881f327cf526a632d133430a4a51000000000000000000000000acf2be68c9447448662c34396bc9e70ca5894f74000000000000000000000000c729fd65cebddf73c183d1757088bd56b110f450000000000000000000000000b69aae295392294893af0a5c3daaf26933dd6b54000000000000000000000000a759463fc98a941d0103df1139654c2b24449e7c000000000000000000000000f6b92633f2c0bb4107890f614b24f003f9fb402a000000000000000000000000c19bea3feabe1b3e10e49a3cc7a2ea636737ef1ddf5bd851745c926689c7ef66ef9422704cb2cd3f56a6fe633fedd0718e465d569af8a9374e79cc410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a75fa4115daac526ea1a5110ee63c427209066c35ea0812764edd1300000000c7a3b86ab9f2401565521b30a777567ee2408b106b14204ecda1c15300000000d1f3da5badb75828905ee0672e4c2e4c8595045200000000000000000000000055519d0fb505977e1e033a7d291b7f5cc806030d00000000000000000000000071941227434872265f9ced239e3d677b468d0d110000000000000000000000004bd3353525c18a42b7703c24ad0e8a0a6344dc07000000000000000000000000c009943c68c52570cd430b3c242de03a9b5b44550000000000000000000000000021f0120f2cd45b77e08d139ee9935a00d52e77000000000000000000000000b12a001a8274b075a384a752197c60439353c11b00000000000000000000000067ba1019ce2d9d1342832a114dde9f258611897b00000000000000000000000020b5240b197ff95b38c8944a176de50180de5f261f5390536b733d11d8c0a42162cd8449b2bb1c04f4357e14e230fa058ca20b25d64da47eca12ac14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f237823fe9aa592d40615206a8d137419b1ed926ebf44246fdeb7b5800000000a65e5a15165b63451f0c8e5e95674d1359d7ef225b8da077c8fac71100000000fe3800541be51e2ee66ac724dc13965d33a3210a0000000000000000000000009443c07da089ef5ce930f9309d4fb07eb305307e000000000000000000000000b32b4061a8db2669096b52550cf66f7c357cbb480000000000000000000000002425f8701b84b05ec14cb340dbd0d379859a1844000000000000000000000000b1688708eea5514389fa356192a69e265437a8570000000000000000000000002b95012e6ff1b744284baa0de7621407863cf8450000000000000000000000000d0b044dc7a3591d7b829204343ac824f2d71b240000000000000000000000000c0b7a099699203d68d14e5ce63dc7681efd482d000000000000000000000000bd6dbb46e7067a20c855756ec441564411d5291a85c77252e8682228425d672779ba5646b27cb9360da2fb52518f1d3e710b91778f4fb07c8d95351d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a242d0fc819f01593c00a59bac1ea7c78f2fd12704bf24f98ee03600000000085d15765ec038d31be49b00a7df21a625b0345043fbe776282fb982400000000f88f8d74c6dfdc12dd1dec7b10c393097cbf7743000000000000000000000000f45b917744fdd67286ce4514f1ee33485ed03012000000000000000000000000cb3c4140dd25a033c556b3304a8dcf39d85e836100000000000000000000000033b65b1fd3b511452e1c364ed0c7347b7a0c6b5b000000000000000000000000371df645f772664a2dab40405f44953136e3a42b0000000000000000000000006d36ce64e41d894f7cf3245036c917170c49dc0100000000000000000000000066f77f7676684d5a392dd877cebf4a412f084d430000000000000000000000002eca213093e07750bba7a34b0f02396b99bcf966000000000000000000000000ccba353e7bb57b3ab8464d3dd233aa64191f7c67a2921d6f18b7ce357317b86e92d2ae6eac35d50c39b4c9750dcb91129728ba5cce85802c5ea44c6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048be2b75714f6139d001d734cb608f1d842e694e791fae5fb3b86c2500000000b5912c761b7e203c231db34d43b1a44a1ab2e53656ba7d138561935700000000f019492a5f64304d4f5daf510e41313bb24044600000000000000000000000008520b00b87894615d7d3195348f6dd103e624e450000000000000000000000001250cd583c217a7279b5e63b24fd193f319c6b0c000000000000000000000000ede5a30cff2e06721ab0a14d46e7b2140ff26d78000000000000000000000000a879cd27782f5d3b03a0f019d8e8d220853c2911000000000000000000000000b8ccfc553831747d4a04bc1a9c619157b0aca63c000000000000000000000000c253ba5f368b3433fa160e02eba55e3c21521b6a000000000000000000000000800b215eb5acee75a404532276f7a86ae6e37077000000000000000000000000e6a87e1e61a4e3362e477b39a90e5741db3e8b3c8ce3660d5c067219c92e7a6b82d6621b06160565afa3763e3b41a0059dc35d158fd4e12361265730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000648e6c13901e63343e69eb3c340e5b0652650e39a90c736af941545c000000005de9c71db3e1761a9f72b17d38b1335b07a62a3009de4b7642178b7200000000c8206228c8ef652d302f405ee942126c44e7f42b000000000000000000000000ab9df1615753586f4fdd301659ba030aae8cd05a000000000000000000000000b6ccae40c9bf317d8bc1f60e6c0a1f351f4b661900000000000000000000000034e3ff17b0341f2cadaf092e850e983ec73413550000000000000000000000002cd9ea79fbde0a1d8d892459d19f736892ce140e00000000000000000000000074963105911e3b34b4953f4875b2d92267e01f4900000000000000000000000027f760621d9b156724cd6c605f28ba0361d182700000000000000000000000008542c36046b7f058ae2cb311fa467d5a05668856000000000000000000000000c1481e4c57fc695aef5f5c5c06c95261ba0d627a078f0534fdd4e975580abc5d3dbcb6512bb8c028cd25b31b5508f256f3702314d6b29d780d8c0267000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dece435fa82961501cc8c96906a8ee1a88b3f616fdb86f1a2fe30e27000000003383802155be596b90dc3a2abfaa707048235a60b43dd54f6969e60f00000000fbde552e95e194601b4bb410088bd57ce5a75852000000000000000000000000c5668524c5a84d693f78111daebebc4abab93561000000000000000000000000e2ae8f5351cbc361a3c7bb3dd80f8146d6df6b54000000000000000000000000ab8a2a50a0931403eed5bd0c852c4a2d7402403b0000000000000000000000003ea7055c5c6a794c59d1b1534f94a42276f97a0400000000000000000000000050022204a286b302673c7100ec309151fc52ba57000000000000000000000000306efd3966e8eb3534aad93a359f4230bd1287250000000000000000000000001a91b82b1817911a15ea603653f62747fa795e4000000000000000000000000094aca0080ed0cc416ab43f43669e636581d39f58b6f0fe1b6d2c87689e34e15588f65b0771f8b63b44d8f72f94bd220adb1d4d4f1fbac66ff3431455000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a6dedf245e0c862f0810b3426c4312705058602e403ee13e1dc88c2d0000000063ad85747f65c161ebafc915a68aef71b37e013e9209fe4294fbc13100000000acd7316cea709e705e43756a7daf5367ed7d9f0300000000000000000000000031952970bac84138fd5e9b42c1ff5a17b3491d2c000000000000000000000000f2fbd17c1ac69352731cee4d08bf953c44afaa3d000000000000000000000000cc59b60bc5c7544e4ba3c518699f9a092aa09a4a000000000000000000000000c1092d51ee119933297c85094238dc7ac842c030000000000000000000000000bd37577c7123f72eaef6e43948e8e7737eea694d000000000000000000000000ed93b47ca71e0a279349c93a3e810c5d307b7073000000000000000000000000e8b82a371d176e414f57e43f27ff5e59e9319966000000000000000000000000eef03d51ba8adc478dd0203eb1194a5fdaa6cc438f23505f707d2556eaecbe10ccbccd7a75be272b5496de499cecbc1ac3c80566d0e8b25cf6700d5b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d86ed6a47277e4e99a7ac01b3a93d6501a63902191c1f37f4dd44120000000061bc3b4095189b310e3ff22cbb73c1715d7c2e62c1204a4dce60197a000000002f82eb78b16f0a1ebf24c06b0a3d3d06bc5d5e3000000000000000000000000001fed9347739296f7343151c9233ea3dbabd506f00000000000000000000000064ae7026e510c7070ccf8b5201b65d6a06478e1a0000000000000000000000009130263b16925535dac412473e8ed5786f41d92500000000000000000000000048eff836f945d850e3983225286f4b45f622b7410000000000000000000000002334d827f340c2454df98858cd41146e02392b17000000000000000000000000cb0cad7ec1b1c12f4f81536c2152c478ff4d98400000000000000000000000007711506648f6ce35b0534050666e8d154b57606500000000000000000000000064b46e1253604e38ca90861c2f0ba501b2f651620138e740caf1db6a6e0bfb755f60af5244d8db03f5ab841d13a6f318f551bb333debc0124c1601580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b47f87ca5f8102bbfafba3ceeba645d42912846c5461f6feb85f5360000000090954c4578c6e437d41e6b293e398f3ea2ad800f3a49f95da0e2e9730000000010f5a930908b6f558f935d659b5dea6748b6f77900000000000000000000000036b833161d5010388d25766410fd617eefcfaf7c0000000000000000000000006b58e93033d9d7667cd15c7b2302ed387092a5240000000000000000000000000d06c2683e48350526dc8c57a65bbb4187c5f23d0000000000000000000000000a693942d435386fefc4df098dcb24293854a9270000000000000000000000009fc4694473072951d32081543c50d4501bbd96560000000000000000000000003dca1a1682dfb672451f872fed7c7973f4f7f5690000000000000000000000005da2273721d1ed1ecef7315f1baa2e7190bd4a740000000000000000000000008f39814814db995b3dc7441be20c1464da5ceb60e6af161c4e3f7204a3c941412f685c0709facc09372c13234814716bac98a8067d66d34349678a1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b7de9692113171bd0da9000aa0ac1723f9c80742a961204cce6057e00000000bbc3a75eb952dc290d54b239672e3b56657451191c136d38b4ad270f00000000692a7669a82cb9595e89d35df6d49372de81a16d0000000000000000000000008d70043cf27419228558f267025a914d8cce33560000000000000000000000009545cc008add5e39272f93588ee19b38a3a0de08000000000000000000000000fa6b6b12429c6b6acc7b0a6dfe77240d5df9a225000000000000000000000000e0fb770e8c3d1859c1860d0131e74a770444741b000000000000000000000000147cb2262e7fb61c6b7b3e2ac6da7a30844b0e62000000000000000000000000e8625c6015a48f34b6fbc3017e3f5d3f3d05f26a000000000000000000000000b4d7ec3411174870a2bba25451b33556d903d151000000000000000000000000ccd975123b083c7c7a30f9670efa72691e7fc31dabe5f84b8ebf422ebc14c21afb21800e7aa58c2636226604a6981c14c784012dac9f5b3dca74063a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009f67d7063833ab74ee39894ca035bd767b3f7e73e6ba8618b7a48c3500000000d4426611a0d7a936eda6a65588271151098f5d6551987c365158e03000000000071eb65e892e661ecf09b93f1ec59e52a4bae44c000000000000000000000000cb68887175bb243eb416a879bbdd355f23ee7d760000000000000000000000002ac3286df8c4fa37e9118068175a3a621caec666000000000000000000000000cfbb3176c982af47bfff3d7ed766037d213cad25000000000000000000000000449d3b22176e0d57a0a8e462e5f4cc40061cb76e000000000000000000000000d5b5db3393b6492d582a00030cc6f3185d6b4275000000000000000000000000bdc23100a3b0124e82584206e9d03c2e2ca2290b000000000000000000000000a0546502d049747ccdfbdd357758fa262d97c52700000000000000000000000013a1dc3d7e5e7b29e3b84c498d30de40e492f2067921df3d8297d84a1382d548181b6758092f7918d0bf513a46ad164ebe49f035c722a239f8e18e53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f840f37d3412cb232753a70ffe28890c86696c6fb6fb850f123bd73700000000973d4b2ee83ae7644a794365277972775095ab507e084a19917b295c00000000ae22374ef0cc945e1b799e6a41e99169a161f052000000000000000000000000d9248c69aab5077367befa6ce658ff4ab56e3b5c000000000000000000000000b940846b1d7a6878912d8e15a5b67d1bc4825158000000000000000000000000ffa9f22ac9bdcb0da7ea63108bac601b0574db240000000000000000000000004b9de979967ecd5a579d7765b401fd725f4f945b0000000000000000000000008bed416333e74c61317f7839579c8d2bc890f8550000000000000000000000002cf5cb3fa1a9fe38e7ec886b3361d5317ae5dd29000000000000000000000000679199482ff8e606597da61e22d52b45c9195f270000000000000000000000002d267a4608d7d0611d23f27bc1efe13fa8d7bf00604b0634578b9d179db332714238174316f66b13b014ca7adeb147264eeb83244cfcb7760b4b535d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c2afa66b4bd116e3a323f30d725f62698fdcb4afd2e756c356e197200000000f6ace32727afef136d7e5a2a48e20b3037314f2031d5714a00f548120000000093bd4c612c8195627e749f77483a7522807b4c620000000000000000000000001929863e2a8f4e0a636fe0268e12c554ce50626400000000000000000000000076590732efdcd078a94bb119f756dd10560a5827000000000000000000000000bd497a742c14ec56e2c1a23c7467cd097eca202d000000000000000000000000ec86e7440abd7f2a6a612c2dfb164e7c2cc3286500000000000000000000000077f8c906ccba480a742ed149d88b1947223a5356000000000000000000000000dbec8b348a46d83bf6779b7acd30f222cc78645e00000000000000000000000024f9914da133e440ad9621349cc0bb426843766800000000000000000000000024cd07147effb9155de15714496a1375d45b6d0124c9c033c5a09d0eb66f804ff0d6d423895d23094689d4308061316b065d2e03c98e5c17742af83000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017084110fdfc301d950c1431b19470637f93d9480801531d154db97c00000000c8f7264291ed781aa552405553926c57de35d158fcb44d5b1426281b00000000a67a296445a05b5e40db3007b483af751aa77832000000000000000000000000325b4d638276f73f12d8c016243b6f6bec8064610000000000000000000000005aa3ed527fe2dc5176ed3f70d0d3f506b98c8e22000000000000000000000000a27ac5556c9c9b589e4edf421239ea607c0054470000000000000000000000008b071d4e822d3b407e3bec3cffe229457c5c1857000000000000000000000000a2d35023cd7c2574f8eda81c05eb247c20d03a5e0000000000000000000000005df31661227ec2285cae770ffe2ba6347597a442000000000000000000000000fcc54b7a1f1b2b5d9f88787a38a9924b72b694350000000000000000000000003bda880a8f634c452cefe5335462ee3a917fdf3a22becd618b67b14ce8e2ef6a4833fe052b5eab7031b9ab28d5003a45d5a7d51ccb5e7e3ab967b20a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000688af27d9441c20a55853099808d86a7a1c3d4c31cfcf058cd24b3f00000000ab23f909f51e1d57043f08337843107225e92e7709ec66107d03601300000000a090cb7302024c6a88d863109b793733a601884d0000000000000000000000001e4870762b413555cc7e4e4dd1ace2172a700851000000000000000000000000c3b6a73da7795e4ceb38622d6928ff49fcfddf2f00000000000000000000000099122a28d33f135435791c30b672bd2f58e4d77b00000000000000000000000088f1662aa7c7c22057befc109279ce266cdfec2b000000000000000000000000ae3fbf1ba42779159b9d0b3a4df06825aeee45560000000000000000000000002a3d102946b28b22df689a2d94f0d170941b30410000000000000000000000008af40d2dfbab13009b2cd310c1d60d2bea0701280000000000000000000000007a6b1b405f6d4379c8ff167c317278325c1bc749acc67e74cc81df29a35ec6598d9ce7075d04532a495f9574def67c4ec7910231b74a397c51921769000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ed13d22efbc3e64eb56aac5fbeea9f172d6576775c0cb946c1250e3b000000000d6fce31a210a832ef0c8246eac7ce75aae8da554b40a658ee2bf778000000000c869c3a27cfe309a88ef71d93a1381db56e575300000000000000000000000001475c36659f8d47b220f828fe75264419f7842d00000000000000000000000007e930668743513797131b0d04116e5dd9c2be0500000000000000000000000057c76a43fc1bf913dbc58948ec8beb371b9a6052000000000000000000000000795c571b40d9672c7d55e87acd50e22bd19d4a07000000000000000000000000cb8b3b07b9cf4233e42fe47c8f60024f6723e14b000000000000000000000000d7f5e7055ae00c720ebee2146038721eab472d6b00000000000000000000000071afb4612b65120108208547f1f73c72adc1983c000000000000000000000000c870b265030d5c7833b52b565fdc1a03ea3d16483a523c5bad1f3a0f5f926302abc2710b426e6e036f0c2f38a030c609c6bf033ad4e9a45090eea6640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bb3725b7bbb297894ca5350155bdf038092c7747f24af3d987fdf6d000000009050da4dbcf22763716e5b598e5a142900b58c38e1545a572c649d5c000000002b11eb08e70ace73de950b6d0973db29cb0d1e32000000000000000000000000454c6827b60c2b1ef03a614d5b2bd02068b01f53000000000000000000000000a92ead43c73e40321d4e670dadff4009bbc9377900000000000000000000000021df6c632a7ced4e3c5dbd7c1ad7a968be1cda16000000000000000000000000177a54477ab6ef3f3019ea07e440756b9c340f3000000000000000000000000035571d6f09a692741e242173cf6d5665ac6ccc40000000000000000000000000d3e7a661dc7a0d49e4f2f675b930b6549e50644f000000000000000000000000e3aa9e73951fbf39f6ba2d5c9c025d167643c247000000000000000000000000d529ee363c61992e77180c0aec0e8e0deb3c552344d5d064c29bd91568614e75a15c8e24a0aafe526bc3aa3e7bf590676449d522c1dc6e4f6af0385200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015473c74e6cd1326e966de2e558aa76ed45d615a7ad1076eee31056500000000ce93797efd6ea10ff7d2bb0211485f1d705711779443b727f068987100000000f6e9f2356fc60912e58d570e2f215d663b920c0c0000000000000000000000008241013902ecad03b0a6116ae0b86b274dc4ad2100000000000000000000000079e6bc38f8d2d56606000729495a88368757d72f0000000000000000000000001671d9675ac93544bb8a1d5cb2a924753627e4350000000000000000000000003de4706de0c6135a246dce5bb4d9983bbe87857900000000000000000000000044bfc71f5a78b8748b41dc776ed67055bbb56f0c000000000000000000000000320a1206346fdf320d9a5762c517147a4e05d73b0000000000000000000000005dbdbe3bb5ea373cb729de6ae28d021e0184fc4b000000000000000000000000724bc175f498062cbc464378211b466197ec483b39ac834c7e9c825caef7e874d8cb5402f4037e722570de6fea091a46e91ee53fbee6ad2f6583eb5e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003664946536fd3927d60b5a56cac87b1810a2cf7a43c0b5018536217b00000000bce23a2f137aa10ae49d2b16bda92c0393323e0ad108cf621d34796b0000000073e185652dba2b1d2763d27df9262a0117e7d3340000000000000000000000006052041072e3097793e8a43388e97e3b488ecb7700000000000000000000000079bcfb3fe3fba1210c3422316c596165c2a2597b0000000000000000000000000d12cb77b54fbb11d32a26158846af63d97d0d3c000000000000000000000000f3901b0233d16f489ce67a0649466200e6f15f550000000000000000000000002c30a86b58ce9d65b969d14775a33649b999964f000000000000000000000000d5855f7433dc3430e30c167e0eb5fc6445ae1749000000000000000000000000264e5553dd4fbb20e00f1b453aa1c04c6be3ba06000000000000000000000000ebb4a15681f0b90bd8d9787e11400025dc8fb234d20da059e051ab5f0b459832277da35c56b5b320cdc5ef4e14a31e0deb1071575b73241be9674b02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cad9d85e427fd90e1140c219c37a38321991a46d4c857c0347eb494c00000000eba79c76ad27e44cee2a7c172358ea25a56d5e286166333967326d13000000009a8d276e68326247b234a36f479898304ee66e45000000000000000000000000f9d0e902dc1ff7578e3a5367d06d9437c691b81d000000000000000000000000cd8483147b888144fcb98d2ccc7a444a046e2f100000000000000000000000006d48c147bde82011e42c1c4482a6665bfd91c60100000000000000000000000056097f756c20c10a5fe38e355292556455beb822000000000000000000000000ea92a3666d9f414fd27782639391c27dce7596060000000000000000000000002287da659201071bc92a047650b104172a24c9310000000000000000000000004d6f887a628a6638566c9f58adeb5248e2696e5a000000000000000000000000738a101a0a2955472547726bf8a1506696fb834b67e0645e21422173ccd9107e6699e62091ccd75a1d1d7c21e7748718019ff52b77927534b047cd020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008923f122a173cd2ef34f7c3cb368ed00d99acc0fd63f8166d8a20779000000000bb8585b4871cb38034a2a6bcf68a7675cb21040ad18f749f2cc586400000000aa79d01a53567d09841a4e02b6d66868a4c7746c00000000000000000000000080ed3605b1b9801f353db867a3b3aa62a9b6876c0000000000000000000000000e4ea4528e1f611068c63651d287ce15e0f1aa1a000000000000000000000000140aa712cfb95a221c18204afc0bf17c9514c5420000000000000000000000007576b1613bb3640a65daad64d5dcd82b88de2434000000000000000000000000bcb45107efaeff600379fa74c01b7b708a1e583500000000000000000000000038c0466a91c28a0f826afd51d7484863f96d2e27000000000000000000000000467d151387b8907a0fa89504ccabd40c823eb33c000000000000000000000000daaeff47c073403a436b786c7e35f820ae22115abca42a5d8e443f69ce78d46d5b7a2a18437b833ea50f237b6d957f0d23149c5e72d0274bc7a5eb660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009714ba15b879190601004c269f149c0a7d56f63e13bf000001a17a340000000004ae1e547202e720e1f5b02a66ab7d07fa16454c52c91217d133b02c000000001c825714af13655bfd536967272ae4338a23ef65000000000000000000000000c61efe5ffd36054c7e33ed1ad2f06d3a44c40c1f000000000000000000000000b64dd1386ad7ad26ae20f352044e9c4b8ac03309000000000000000000000000c257ea426a54ce74ad854c2f0fee2d663ffccd07000000000000000000000000873bb64e96b99d36e6f2230c0240f46d5da3620c0000000000000000000000008095c40df7baf25fddb9850cd85532452c710d360000000000000000000000000af89f269ae4f55eccdaa5326e09064ea7d3e706000000000000000000000000aaa17754c213686ab8094d6df86f40358d1301480000000000000000000000003474e870af2c4e7e90d13d0a75e0bd22feeb1123f83c88782cdb1e0e98269e38fe9cad1f27c1363daa49b3288a609c4a706ae817f41d032cfe8664720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bb73c5b02a336349733e8114da7421e03688421feb9824687c508070000000080f0a1770cb0fa277a06ff01553b830f886eb52f02ee12605f0b523900000000dde85329b9a91262a8dc732574899912ff127f2c00000000000000000000000030176f596e3ebe7a9466037bcffd5447e142d56600000000000000000000000071897c3cdd63271337a3d6404c3d7128d632092c000000000000000000000000f02aa44efddc5a0f101ca3136660df053bd0f618000000000000000000000000523f0d16b281090939f08308a8ad6e27c19a2526000000000000000000000000a7de2c16d41c00202c18a218d052ea5719efd2340000000000000000000000004b9c9d14c22e69175fadd57ed3c87f341edb55610000000000000000000000008b22be25cbfe7e6cfb408a1366270c340449316800000000000000000000000017f018152ed14f36811e3f45816a1663fb84461400d1dd17685e191b6278eb57d3b05f104b72c85ff8f3bc13619c174df9d4b9364450881ab04035730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002012b3711d3f7532419c5d2532c8ca449abdb35f9ef22f0e8e5d4b09000000009abb253e06fe570b8cd122755c83c21b91edd274c60f2e1eccc7a7580000000000fe154e8e53e06470fb6013bf45c96345e95025000000000000000000000000998b667edb68bb051c0a757274e2912d2366261d000000000000000000000000620819464969a0493cbd7a2f18d0810190ca0a590000000000000000000000006840c119bddfb918cdba0446d33d3b52acea486500000000000000000000000040e25400f3b39858e1b55e1bf0dbb97311bc0a01000000000000000000000000ba18857c7644682aa028834c26187a3a7f20253b0000000000000000000000007f8a0548350efb74b7b3f46e63b5821f4b03b875000000000000000000000000b0e1173228a2055cae260c1aa07f46403f12c82300000000000000000000000018c7466864e9594a23cf6e49d8b9011bc30b0261e2338176a4a3fa693c28681da4052d1da80ad17a52c7ca70bbebbd204be7544432afbe521919bd0e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cb13e2429366450dbef86175c38ab5da662fb5d422ab1441930ef5d0000000008ec8d2dea85e0475ef36f48425563224268f3124034d66cf5ae317000000000e64f0c58d6802a2746631235fe721b507ec4c84800000000000000000000000053fc0861932a81411d3e4a523203344f666b9a1d0000000000000000000000003a3f30526626f70d6285e1263715ec4370f8625e0000000000000000000000008188c207ac67cc615af4780f4a9ac168d729e93200000000000000000000000098103e709bad1a44ae628a09ef71d71bfbfe5d620000000000000000000000009f68fd31a76fa5361072c979341c154f33d67b07000000000000000000000000b112945f1a87a40faae8630bda310152466a376c000000000000000000000000ba134f518572bc74b60ec1313d2ea6423ac652430000000000000000000000005cc3e706e63a403ad0c03c419fce0a4e4356381bfcbf7c23fe60eb1e84c02443e911a06869c5df79317e4625601d3e26173ebe2b6a9f43314976c638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f39ea04fddc9dc7c06f4b65d7702144f516f7f7a55fd6b753450c43d00000000601aaa6d227bc65ff521894106b3b95abae99771ca76fc229961867500000000124838218a087c1906a1531b4374e3455e906240000000000000000000000000cb80313bb32aa361be26c40ccf4c802710b38516000000000000000000000000107b934ce1d0c947209fb9507490ee50629f0c3a000000000000000000000000b724fc7198b16d24179a731a51a01c5775e084590000000000000000000000004cedfe375057d039aaa7746abf92be4399b62b2a000000000000000000000000710bf613ef13864a14746604471b7300c599693b000000000000000000000000b5d12c10d21335680a83b56da947e26b7e8b91200000000000000000000000008a18ce518ea5945a80c15c0c79bc704e4f0f14180000000000000000000000001ce2a52f3b88dc1f04bebc101858b5609834675800dcc57e64d3db56482326773e755a0330d5372bcecaeb39eb4ea21e25c1802b0922f42e8137b6160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f86942cb923b035afb1a4544d6ff2520f9dbd7d3610f9187b80c936000000006fc62d19a34a5108da5ca04d9f956535c8fa0f77c129b20dfacd5427000000002334065e4dd552184211583c6abe504efd15491c000000000000000000000000ff13615aa9b4a05270de48202e862528e91b2027000000000000000000000000fbaeda4a62f367203ef3fa75956256336bfb6b2e00000000000000000000000057120f17be355616ff08a00d0b7e9e1800b88a3500000000000000000000000089719548f851d6368393e15fb71b79493f698c09000000000000000000000000afc5a1317def933c9201677312ac7a5bcbcc030f000000000000000000000000222229480331a85bfb2c946ec9288237ba46b92f00000000000000000000000059732a73f6474c2f8d4c06674b673371b887127000000000000000000000000083cc7e025e8a360d35b8b473efb31225bfd0bc6c0067da6632d81d29d7105055e374211d90dfdb5ef19ec64b04df5d0bf0e493107023714a45dc22190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005918492f6e710f0410ae8b31da58602df94d27362218e4427a36203c000000002dc3980b4048cd4e74a3373b3448dd3c1a4cee01f4ca4959d0fa294a000000000a45b30fd4cb333a90034a7d956fb62ab1f1d94f000000000000000000000000c7c6f3496d2ba30b8601c60e34df997922d00c5d0000000000000000000000002e66ef7e2b1969737ceda95d36abb878c127d01f0000000000000000000000003d24a5324f04f1756dc8ca0d7e2dca6d2034845c000000000000000000000000145dff61a21262654427700eb6c0e91747c8bd75000000000000000000000000110d7c62749b9450cea88c265d1d600579885f3800000000000000000000000061c91d44e8e898538d28950dad89d32a44851e09000000000000000000000000c1613f465aeaab07a6470e69f00e5573002c495c000000000000000000000000e3f9a63b892e4f29536b54393e733d568004e3464e8d7523c36b0e61f9cf445bee7bb77aa6f1162577899d252162f805ea2f2b008fbdac30b2126d43000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b83b127589b24c46eca6a11e63cd1939b1a8ee704e1720313560204a000000008416ac6ab7289b38e11b124b6498e60798b02776c31c011f0bd4a12e0000000092659d4ae54ee735b9d14a245e3b27177bb5b96600000000000000000000000068b42f50c45eca112573762c2f35900e776b100f000000000000000000000000d05b0c64f7ba0b7b27783744f2e69465559b9b3b000000000000000000000000725b93055ae85138c609d854b891764e17fb0c72000000000000000000000000fdca611b7509a518476e9438f9f9135b5520714700000000000000000000000015ecff76f5304544089fb974ecd88a389ff7bc240000000000000000000000003894744c4383092bf777257e3f6ba903f41a344c0000000000000000000000007dac0f42a3c78a77c792c97ecb73953fe99fae560000000000000000000000001f7018577b8854488c5a0d63991ffd1aa5f816124e39ac066cce20245b1059598f64c6583e35226658c044380b33f064841be615ef32d90c8606c8270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f030449058f882f3268176938e6940552d60a47baf5693ab670257000000000e264cc165f62205afe00f570fd22ae09b0120c550eabba175bc2616700000000e2f08718b76e194ffa2c773cf7fa531dc9fa872e000000000000000000000000f584e90762da714d93fb7b4e1ee7f44ff2fb5e4d00000000000000000000000030ca5122d758d2461276af7d08029c2cef8db032000000000000000000000000967b441ad687836d09cc570af2f404662fd5472d000000000000000000000000fdb7522c89d8874e529d723619e71c345921a43000000000000000000000000058a83302b37ba617993e9901e131d6192e7bf253000000000000000000000000fb9d33393f9a8f2b12b46a48670eb80f994432120000000000000000000000004b9011590dae9434c1f29020aa5a8369dc150768000000000000000000000000ffc13b0658cb824b54b5114c3de15370dbecd4146fa8c61c1e0e001c2008a91e4c90da2118d2de6cb2381972af8fba34506c89359243915c277f806a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c59a20d30ab112b8d47180dfea2721fc1fb1b3208149276d588e63f0000000070bac00f0e805d103364cf486078ea4c1ea9d94e6afad42d9c819e3b000000007cfdf8149e604b78c18f7b197017fc696dba91660000000000000000000000006372b139dd38861d2401bc52ac51705def2df4520000000000000000000000007adb1753cd1ff35b5bc1a2350ede5a51ea0986310000000000000000000000001c137a54ee699817d42e9509d3ae083f6fac6c12000000000000000000000000a1f7357940660a435b1a231b3d47646836c3642f0000000000000000000000000679367ba71b36119cdda4218951c11e5119145900000000000000000000000034c00f192719d8178c483e31b567581ea0ff161c000000000000000000000000ab423f6e61899801662bd04a4f90055b2705ba6400000000000000000000000066b0703674fe0d1400650c4d7ec7d343e4737c4398d7c936e499ac11c815e545c63cf071ab62ce2f72a26c61bb444b11bbd91b0b42d869302e3b6b3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097dcd906ff17f557e5fc47737597b86198d6295aa779217d5884c67800000000a7309d0405c55b5ad403d936962b676b8544e13ef70e54020a09f54200000000f798ce39e21e79057a149a7938396d496bf44c070000000000000000000000008b9e285e98f50a038c43103c3574125bed0bf15b00000000000000000000000025c55558515fb905511ddf5f11a58667b9a9ed16000000000000000000000000bccd831acf15785fde53e345883fe928d07ed13900000000000000000000000070c85f00afadef13aee93d6aa16642775b03186f000000000000000000000000aee7351a3493912be2e2644b38543f5430f4902d00000000000000000000000041715b3491036f426b23d25da71c2c763fe4354f00000000000000000000000017e64f75f151df5c6912ae2d37b6877532be69300000000000000000000000004e50db56a79677092ba7b7016c6f802ab69a25633bda9709fe9f522b29c113537e90972673c18e17e557c266bf97da42942e321f42143b7c64e7592f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000515ea8795797c1319155c54bf57ac72346dedb31b578892ad811f67500000000ec4b3372d7f0ef01823c1776556d433884835b3a92c2c9044a48111b000000008fb09b2a1f67a81d352bdc555986fb0c9eb2e773000000000000000000000000416e510869ed8419e85632150ef87c4acce71326000000000000000000000000b8bb1f380dfc705bb90b8b1c1460782985b3e5330000000000000000000000006c188f263619b96157172332b0632877675b0855000000000000000000000000038ea10206450065c9a7081f71840b1c7f955061000000000000000000000000c2b0ff6e67902d0ca2f39f2e3aeb557157aed12a000000000000000000000000c770d475c4b7510d052eea6611197f149ff47b140000000000000000000000004b1fad5e31d3a52ed3a0981eb4c348260cea925c000000000000000000000000e679381763a43a6997be8202c9b9d7770345d05ca4245b299e63bb6cdf153416a12fea7a7383ff3c4012e8247d6b151ed97ea95dfe751a3b4430593e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fcfe2f65a5350c1b61a6c5011cc0d21886548f7ded00d518a702a257000000008d7d1c2aaff03d2a24424971befd4f6635667c3955510521abea2b6600000000cc2fb97eb9eeba76f703ad5d4f7350079085f22d000000000000000000000000675fdc6602727172c18389394b3fe82a1f06cb1c000000000000000000000000d183a2103625d72b3500f7712e9b483e1a7d061f0000000000000000000000009004c413d710c92408a59e36e95fba4332747c12000000000000000000000000ef71696f59757c6c3a296e439674fa5860355e3600000000000000000000000042d5e44d0a3e983e6e8f5e76f6c06f4e83859f11000000000000000000000000dc2ebd60ca2c7a73cbf6921e09196870aeff542500000000000000000000000081e04b4b5fdec86f05baaf2b4506b115d56d99000000000000000000000000000a731a144681fa0d0de4755972ebf64e2e41521d45e53b2781307201960106129d012b5994d19777e21c4b36bc40937a732e59082f36d8299971bb5d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008dedec3925b99d33e6d7ee191e74922d0263201daf9c1a6e09269e560000000091b6a41460b1ad2b94f81214a3ae4f7c8bdd830f94bdfe08d916ea0900000000848d366b4368af503890782d5642386b20f07744000000000000000000000000e531f640b06945428c537307214219597500df62000000000000000000000000d0424d601beef42807c2d91e945d490f1d0b5869000000000000000000000000ab0b3f72c6d01e2069094a17d33c073d5c3a8f3c000000000000000000000000ff13401e18be113187cfbd119e4595187637ec5100000000000000000000000006a63f49c432ab035064570c937b054c0e856837000000000000000000000000d104e70d697fba5820adc270da3283149590f478000000000000000000000000748e3e7b74a965507caf2f5b5b939d60a0da751a0000000000000000000000009333e55a89d042674a4b426a3b4e7c7a7f5403597cd2d6330eab2a278a48b50c21a14e599bc92f61c3aa0251ce72ef176d41307469f5b50107ae247e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e28dd4dabe4141df559d17d7ad1581b0061151e09ef7a6f15c8d747000000007e00475b9992a574247e82476372974606ede8139682b2597676082100000000ca4128509ce8f40278d009532ef8071b2e96b1420000000000000000000000000b40af2639cb8d3e18cff2685a40b96afbdc7401000000000000000000000000859ca516cdd17d6443c40a3eac8c9b2ea419c90c000000000000000000000000bd15983748bfb75eb97c3a5af5038e70eed46155000000000000000000000000ce44a419c9c6716dd956834001293d333c70350a000000000000000000000000459ee55a0bb3fa0f1a9ac32ae31c0377c0c164110000000000000000000000000f3bed527e6fe06ac849836ca038c91fc17228320000000000000000000000000294e92ba8c162774712d1624ddee96963acdf630000000000000000000000001d66472ac9e1d911a0ca151011d4fc26869ea01e1ae5820d5c0ea946c4233b4e96c1091dd83b7141dce0bf1179a99a5a1bacbd71d83f9544e99d254f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099313a123cec4103682ac117133517547f0a6e20cbe4b22834eba23500000000927fb96862aa0e08012aa2117012892d31d93861ba39a053bd9a103a00000000478876291854352bb925222853bd19461322a77c000000000000000000000000938edf402266c452f4056a584535835355001874000000000000000000000000d3312c74a6925a59e19e7a2391069b3848235462000000000000000000000000818a1b776559ce172486637d3f5c217386f7be0d000000000000000000000000dc290a6107cefc5fe7df6352681bd11878b3ff62000000000000000000000000ffd8434bdb8b6a3bfefc416aaf61613ae6d39e30000000000000000000000000c9888a242547d11d5d41aa3e33e9dd6854745572000000000000000000000000f469323bf082b820c2c528194e4b1a6aef99b846000000000000000000000000c9595d1348e9174a9efb06162bbbbf35b8b6064310f0ba457511200e3fad6d2576497f1f81333504d1409443304f59690f2457271e10070ecc612f44000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4df41573c6f9b3ec40682147a2b8c59fafd247d6e38bf5ee2fcbf790000000043b2047a69a537600d097a736d86e7174b7d3d583c4da94bdaaa770c00000000574a21162c29c32789878b37061a7c0c0cb2290400000000000000000000000068356d02e88a6d6c9a4f5c7b28c83e4d5b4e3a6f000000000000000000000000b5bced0ae249e41ec8d02a12d3d224154a06c90b0000000000000000000000000c70896bfe71fd0b04cfb57c9e76900085b6f954000000000000000000000000b05496382367936a4378a8042ece09794f3cec090000000000000000000000009048d60ed14c3810e32383262c1e2055d1b038460000000000000000000000008eff1611bf112a4bab5ce63691816a6fd52f5916000000000000000000000000e8d8675017e5dc61cb65ea66058c313de313cf14000000000000000000000000366337009f4b18010fe18c4471f6515775712a506c55d7631bd3cd036ec7c1567e58702b1923f30313b74d363b8b4e51532cd430aaed61746d7a21130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d53d946d6d64a6d160de93f02e7667a9b8b3254e914f32fd4b07e0000000000d6030a4a8e47560bf6a17b6e8d67315dc94c49151a4ece60ede86d30000000000450f61c5c07255e59ac6550e5466c0dfb9fc65700000000000000000000000007b9c41cf37ba73f0b3b9104f1a8f276c14c695e000000000000000000000000a5d9b8026ce6396ea7d4aa4b81130464b2c564440000000000000000000000001cdcaf2fddd4f24849d703301e31df790f44f85c00000000000000000000000061ca4c24e6a64058a666110fe637a90ef76b8b210000000000000000000000005252c95b4e34b44cada7565e45a7b052fdedfb4c0000000000000000000000003a50c8376f98711b112517062c638431979f9f4900000000000000000000000092de2547145c6b3a07c7f25c23834079431f6c5e000000000000000000000000dddd281f11ea7c3a78055b2d3901da1f8b02ee546ddcb90acf896a4dffdb3f29d7f5080b9b60062167f46e0790f6d94d575e3a79f0e6175339c19e4f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ab6a45cafe16019459ff37b4a23371725a5bc71eb679b49f6fba7000000000001fcd640c5935742300455576cce2d14c3e9c67d5eb39d39ae02cc51000000007b6f9a460c5e4139d42f1715f75b2755f21d8e5b000000000000000000000000ab7c234ecbb82c6af43bc20d5d7c036fa7f7cb340000000000000000000000002617180db836ee5aefaf6d2de511816d5fa44909000000000000000000000000e1aed935af81271ccf465415be37e9652aa6802d000000000000000000000000d20ec4735f13dc2a384cb074a1c9d010edc9160300000000000000000000000057f6ca6c68f3031aa7665b460cf6a43978781027000000000000000000000000bb8db90ffc998350e3732936ab0ddf4ac82cca7000000000000000000000000080e0d65943e5b71c075470293c535f1287b34043000000000000000000000000a36b936d5f8c6e299aa5fb1decfbb434a2a9d86018aade3d088e9f44d1f8fb28991d34586a399f36699ca53169f8d12611c21f6882a7e90822f28e6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ab6a45cafe16019459ff37b4a23371725a5bc71eb679b49f6fba7000000000001fcd640c5935742300455576cce2d14c3e9c67d5eb39d39ae02cc51000000007b6f9a460c5e4139d42f1715f75b2755f21d8e5b000000000000000000000000ab7c234ecbb82c6af43bc20d5d7c036fa7f7cb340000000000000000000000002617180db836ee5aefaf6d2de511816d5fa44909000000000000000000000000e1aed935af81271ccf465415be37e9652aa6802d000000000000000000000000d20ec4735f13dc2a384cb074a1c9d010edc9160300000000000000000000000057f6ca6c68f3031aa7665b460cf6a43978781027000000000000000000000000bb8db90ffc998350e3732936ab0ddf4ac82cca7000000000000000000000000080e0d65943e5b71c075470293c535f1287b34043000000000000000000000000a36b936d5f8c6e299aa5fb1decfbb434a2a9d86018aade3d088e9f44d1f8fb28991d34586a399f36699ca53169f8d12611c21f6882a7e90822f28e6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006179a211edbdae39b218c923f28f68206981284abf17880bca37154000000000bacfd75fb2011417fda919649358de68abb2b6439c61893ed3f9a635000000007a96db73d32f6053747d77430249d05213759920000000000000000000000000e128e9034363b60ec9542d3288c2993ee6b92742000000000000000000000000dabe6f05af8e523af0c683485bdf706f2094eb16000000000000000000000000d2b61c1b79b3230723a6d253d8cdd11728e99c410000000000000000000000005809507658854a3c8afd79564266b56ef9499965000000000000000000000000e886f578c9101d308304a140ccf21a05bd0370300000000000000000000000003e81611f44a831195858d30ac656232331d6ac4e000000000000000000000000d2ab6c12a51fbb4295f6ac3c1ea7cd4501b4b710000000000000000000000000aaca7869fc7d274f54beb415a00a7213786a410e316a676af48cac272e198a1fb19a317e25ca4a70b1c0cd6051b3d00c3719d90854d1490d4ccc3c30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af37775a89b8ab366e42ae0564103207a392c6004fe1b77bd55e681a000000005d99dc4565724f69014de27ad85ab407968f3d150753382fec2d362700000000421fc56b8ecec6000d770436e06afb5dd22ab71800000000000000000000000098845d2434c31e4730ffe15fd8b3043eef5cd4620000000000000000000000002807223a3addea305adf4b41e97cf26179690d0b00000000000000000000000024e6a97a1d4beb48e11e8575fcfabe6f0282540500000000000000000000000093826845cf917f0f33ecb3030fc28a700a8e1771000000000000000000000000053532590e70404c8e5e5545d0fa4965ca00110200000000000000000000000039282264f446b17d8909ee29e0f6a43fe7bc1e67000000000000000000000000e864c43dac25ea5078d1286b94f5fd2f3028ec40000000000000000000000000df6f9f66e232e145090ba4126f0fb4474709675feefd0e43f88f5a6ea6e6c26f52bea936e2399e04d21b7e076389cc627d1b9c12f88681748fb7d8430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e724a681c519850ff34083144ef495a3455a8217bd3e4519dac7a6400000000cc9a2335b89f61159ecd5c6218d71010eb19985e2d79be423a4367590000000001f98230949d8b3706e5114db9eaa443a13bc15300000000000000000000000046617a3bdfe78330fcb89c25153ebb2c55cc6a32000000000000000000000000c0f1f05c88cf781d754ad56dccb93f47776668540000000000000000000000009134797a51dd7e402d93ec6c6f295522838ca631000000000000000000000000895ae603474c95058283b82ffab118081537383300000000000000000000000028000b75aafc827ad929d6147447db5d7e325e4a0000000000000000000000002817e0270c74c14d97a2531cd4de4c2286b4564500000000000000000000000019de640d15647a78947d8f5efc31eb428021bd7a0000000000000000000000001dccd346f8e7db6634547f225ec3864257bb1d07e049cf490514811e0a97c34c2d6b0c334b23e26ff6196f28a684f52343d85f6de6dad821cae67725000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000266945041480fe4242cb7171154b97019340ec40c5f92c76df74d02500000000c2845b209a0b0e58f57fca1248bf5063c646b06449a5d823dc69154c00000000bc47cf3fb42d115c6ffa7d40e786436952e56c4d00000000000000000000000050ddec1edd5bf734bb4c875f0b939c0dd1d87401000000000000000000000000f5388a54c2addd0dbaa0a07b7809e40789e58b1e00000000000000000000000005f075406b86e649f0443b26fd32e20b926e3926000000000000000000000000b2685113255f5c65730c6b3d25a545014defe5510000000000000000000000005267cd27e0c6af6c22c5ad164b81777c84f46b2a0000000000000000000000002017b67ad66e532ae1d3451c4b29956c63c5895a00000000000000000000000020c6e90607b3be33795a914887fa831b516f3f700000000000000000000000002831f377ded8b852803513090caaa30713bf5b659b5bfa00cf75c96dcd653838764d892ccb7546187d17a932043d91483f4778247db27169c78e456200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070a20805de07e214ca3e16388b1ad6175050645a4365b90511a45d40000000007462c92db0236818d906d872cebbc765b4870601164a18113e726f3a00000000b0743e212ce6102e6520a016b3d442236ced442400000000000000000000000019f5ae1024ea0d7ea04cb07082e64369becd5e3f0000000000000000000000000ee22b304697531d2bd90e51187b60008cd55218000000000000000000000000b55ae113da505566c7589265b528b3274f533c6f0000000000000000000000008bd67c49b117bd0e98e77e55dcc47a5f9d54c6550000000000000000000000009bb61771e3489866d46c6073f110dd0418f9a301000000000000000000000000eb8c1c2f7edd833c5df4c4551fce4b5ddd75e1130000000000000000000000006815c90ef90c1d4398391379cf706b774c372f7c00000000000000000000000076a1a42ce8095067a4a74778225ac743ac9fdb3289ea5978d18b654356dc341b0a377d6bd63ec74076953e5b2dfd0d5d8fc0323281692843a0b5881800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083282b231366fb6d5d25a100559a7c7719000966e9d5f52178f4f1690000000059afa334541cf022af19e16a94c9620f0135171bfdbe1310c6d4b636000000002ba7eb052f174b7968330732c2628651f1e72d2f0000000000000000000000004e2162148e5ef50f6b92003ef0fb941f4906ad7a000000000000000000000000ed10cf247c0b40360855077023967168b17a424e000000000000000000000000b7db2067a151e173bd36b80954e2111ce0acff68000000000000000000000000c1701449f6d830421ac06a21b28eac2eb1de742a00000000000000000000000097f40d27a5e26233ffc639018c6a2f58c72c452400000000000000000000000029b6b93118e639783b611f2762b32e52a69a273200000000000000000000000014838f3bc59f81074b4a847085bc8732c4f4313500000000000000000000000077054d6a19c00d4c95116933a6045578a1277c5a08848a1820a9753f56c1865e62320110007cf15ec387fe0ef17fd00ae4457820d82b7215fa28e14e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d014f653fa3cad71fd82232b1a4e3616ee37994ccd4f247d554a084800000000e0ea574480ac1428c93c59048364a52d08987b64b60d2b1871fda45900000000ee834a2d219002188ef1f03aff85683c171e0b170000000000000000000000000fdd9c09753b594119de926d240ae116be82f4530000000000000000000000007ace68554d492d1b78e09b766dd1f07595533a70000000000000000000000000b673712da203f713d761002ea54afb111e3a034100000000000000000000000028af32509f93d16fde91bb315500cf25c6b0ec35000000000000000000000000eb1aff742356c04876569b21c39a534ac7c7746e000000000000000000000000920f616888a1d9565703fd5346267c501984801c0000000000000000000000007f27fd468648f17acd56653739ff4e5aa5b2881200000000000000000000000097bf5e7a87a7be18fe051c43d98da708073681105cf6b85f004e2c0a7ad0981dcdefcb64508f6e4191db6768b2675e00a923b26ee798c50aba6cb124000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7ff44269d8e02550266901e1b0da21d520f36122031233e9548fe0000000000c3288a73d6da451c0048e66ec632df1f650dd35d9304552c5974147200000000c4a6305ad80ef15967ae5219db0d7a0a4520aa15000000000000000000000000e950fb4a4474ed3185112d203acf16349248284700000000000000000000000095e7dc40837c9d2e2318b14bd5decf58fb78b823000000000000000000000000f7a4954c97b39959807c903a32821212cff0c20e0000000000000000000000003262846ae43541012de06714bf208413413db377000000000000000000000000055c4b541e1b9265d5d99565f0a1fe0909496a530000000000000000000000006760c019500fd917044e56297b4b4843ca09024300000000000000000000000051c40c72ac217707e0eb9b3bf5ae813bacff990b0000000000000000000000006a4d6e00b9bae31abb27b80c532b1074fdf6f333a13a1b539acc2569b2d128485df0ef16f410dd6e5d208f26b9fd2e3fd353e03d1e42183b1d37a92f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b574a1146b111950a484c321f6d16f55558c1172c8339f0ceee6d300000000004a8ec02ef4d25043b398b21742ebab13a38ca414bfbdee510e7d6d06000000004757ae28f906fb61602a944a6e9f697bfc17ec0900000000000000000000000035be9c1e8d9d62283e4192170a919a566ac1bf7600000000000000000000000019d8ba26e1228b4ef839dd079d6bb8105968b844000000000000000000000000e816347c09050d22997ed33c44690d029122ef32000000000000000000000000717fd252b8b1611e6592f127d37ae97286f20251000000000000000000000000bbb1ff029c8eba22b340d43f9b8e840dbacc0772000000000000000000000000574ebb36ae742459c234111c7830735f98ad5a39000000000000000000000000159c4c6208056064a02cb245bb0db16d390a5c2e00000000000000000000000031a05001e35c930ecc7bd041b508ec142168f6504b44bb3853eed7305c870c04f430b3153521750643df2523f9263a1fc071111dea554f44c1f8b623000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4c8d5683c9ccc23aa4e7d040729f03e12900d620f2ce554912ba34100000000d1f9fb0cb343e322161a092aa7923f3774a9a9353d358b2b3a32ee30000000008b01f463bb29f918ca75ad7800ea13628c09432e000000000000000000000000d4f4fc643937455871f1d26412e2ac203766ce010000000000000000000000006187220216ff81456123056964f0837b524cf915000000000000000000000000c7b2be7575f0ba31fa3ea665cca5d3219ac6b6500000000000000000000000005ebfe71a9dcef634ba173a7d44f95e483b10556a000000000000000000000000882a6e25c3cd040b1397bd3980316f2c97dc901b000000000000000000000000de7494386df85e4e4753bc17d1e4005d4b743642000000000000000000000000587e1f4648880f4c5c92125e190ea95077c74b46000000000000000000000000bdc79c09aded1479cf620e06c73ab249fcfcb018955231350d3ac67dc62be96327098164453180590cfa66364ec4a1627a544665aafc5045703c9107000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c715224e26d8cd07be9050174adb76742747f16f080bae37250f047e000000005183b4350f030528c40854556b68416d0e57af29ad1ee34aec1f7e3f0000000025c0257c714bbe7d0fe6e830c8854e2dfe87ce5d000000000000000000000000bed40c4fd848b34673c7976d7490e030e0d2355a00000000000000000000000074a69e6afd32a42862b448055263ea1ea532df7e0000000000000000000000007168250c5f57221a8eb8597bb1329e5fbc57be15000000000000000000000000850f8026a547e37dee9a00053110c367444ec45800000000000000000000000070b14e2330203d25992b5b00e950f273d475424b0000000000000000000000000dc680517444305f8a105b0344cd5a41599b0c5d0000000000000000000000002952b83822966303c922402d1982223df5b0761e000000000000000000000000c8cebf4ddfbc6c644b2c0d79d771364769717d3f2032e5502e5d1f0b8c480e4813b64d1e4766487db0c98a33fdc0dd027ebe94033d6b8c77d946ee5200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051c0ee7101ceaa6c38461932525bfc3c7550af52a1c67b3084c7fe6100000000cc9a0b1d94826d3ca595384a7d514f1924e631434c0823083a74751100000000a7ee66734be6c3244cc33419653bbe3a1eccd0310000000000000000000000002043c43196c915789d095c2bfd7a611b4a99bd2a00000000000000000000000003efe640d7712475579986783e35310ca47bfb3900000000000000000000000055aaf3202dc7e43045e5f814d0f0696230b9dd000000000000000000000000005b9714645cbc2a3051cee72112b4ac5fc1334f4e000000000000000000000000e1de6d346d938255de0759612294850e6d589551000000000000000000000000a301e748ada8164266b2620793f9402708674929000000000000000000000000ebe9bb3ce3457f7687fd84333d6892754d145f42000000000000000000000000df835b1964c2ab7bf5ef361089e2ba1681971537176267251250c82aba811a22e5742e1e1ade367d3a437573b3e7c35f7636bc04ec34905e10470600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de79df4031cda426cdf82f3ccf6b6f45394a3172f4d75559c0eb40220000000014ac92695de75b18afa54670fb3cdf3343d9fc0478fb3d1f58c5454700000000099fd43a0013ab5531706861b2d1cf2185aed81a000000000000000000000000f09cb32272ac1d356bd29a5766def6297e507469000000000000000000000000797a3a380281092c751d11765b2a47419cca322c00000000000000000000000053d17632102c01115b63d528aeda1d653ea90e500000000000000000000000004b85ca7485dcf4371ca0ae476d2e837d5729a4600000000000000000000000000dbba74a77116e4c2bab2537f8c7ea29f2d04a000000000000000000000000009781117d0ab800186d3b95670ae55d77d3145e1b0000000000000000000000002e5e0c21337ec678f30d92222d356049f4854267000000000000000000000000b5fcb84453a529324887450269f89d48d9d8396ab02af21b19401c10c8aad5316c135d7330b8a35d759a80793e4faf5eb4db3a436715dc7ac62c4838000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c82b9b34bd1c305f6961e475a434e2259a59b563fff6fa4e838d7c6d00000000bf431c103fac1915773fa4671d18a734ebb2406b5d315759e525e25600000000d9ce551f6760a11001074e4f360c9807d12b25080000000000000000000000006004922c3d33a922e6ed5b6980d0d857894c2921000000000000000000000000af6ec0474fa67362e58857225ca5a5496a74ad40000000000000000000000000d626b05724865b6e56e5d258c5b6b757cc93942500000000000000000000000060b0227e9ab1251ba020a34d4dd44f1a51cb940800000000000000000000000091c06a66c456305bfce1cf495ac4ae2c884f57330000000000000000000000007c672f44a531154f5d6b1d75bcb9ae39fce534010000000000000000000000001aab32032f95f9672da4bc5b9b51003a3e5e6e3700000000000000000000000011cb847df6ddcf073d98cc4f8be72c66dedafa101fab527ba30e4c0858c2be5b47d9967817279504ea3365116eacec6f4e2ae65930f4a4685cc72d320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009dbab937289f0d5463dcc80516c53d720278e760ea03702a07efdd42000000006898ed7d99ba146a56a9165f763a5e768de9b836b2cdac673513ea0400000000e050252492493f1ba984cb74b22deb11c37f847300000000000000000000000051d2bf2aaf11ea74922191205717084e0c4d6a740000000000000000000000004fa2291a0acf8e3e295ee52b15a0d44da237b42f0000000000000000000000008c331572b3a4816dc4d7e87d1ecee135df75285b000000000000000000000000f183475e1fbde10707dfbe31409bfd0db9e15436000000000000000000000000c1a1d22713aa7634d8f66e02b4aa5b1df87abd3d0000000000000000000000001e5048539c40eb5dfad00c0e6a394a4b68d2f4630000000000000000000000003383302f5289db7cbdc9bb7ade0b9a4ac3336b5a000000000000000000000000ede13575dd257819742fdf40c178fc409b5f3527c1aa774450d8a7045998865a9bee4a3fffb758324cae8c26897c5b3816e91a4b82f49d41e12cd90600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034a3c8500b081e68970fdc09769ede49f1ad44053532fc0b0467575a00000000d1fdda28759c0615da13a0398b3fde7b73f4301774880a4a7a59b24f000000002e33d0069bf8055097624709af552d551a07004b000000000000000000000000d1e40b7ace02a56cf29f580f6c1a6537eeaa693f000000000000000000000000e54ae23b4ff2e423722e4720d7155c0d2c2c7f65000000000000000000000000c4afd327c75d87675e47c97cf94c665c8c8d4b510000000000000000000000008a21820a8aa80405f38455347dd3546aa6a884540000000000000000000000006a47966e3bf34116b796697de093ce132be1a7750000000000000000000000008b6a505b029b375159769f1389c2d11f51383c2b000000000000000000000000f7438b3ab225455cc74ba146200dc678649f3a1a00000000000000000000000030dbea13345de421fe97825347adac37172ec876419ba570fc9d4a50382bd570102744158b459b34f5b4346e0ae39b30216a1013b84af26464862916000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028a2f32d3b10e3ec317572676e21057e2a1192b16c3f57e4f83207300000000915f3f684f1bea1afafe7218e3bc8348843b5b5aaa600509d045974a0000000076b51375394f7e0f5dad03614ee2c0638c1160580000000000000000000000002f384e4c7b264664d6d95b6486c41e705960b23b000000000000000000000000db54896e7609fe6a4ee790607c9f914a00c38e770000000000000000000000003302eb5c21879b632004764d76a15a450ccfed2e00000000000000000000000050c7142d3906ee08b70d7c26f01c6b75ac26c029000000000000000000000000c3ccfc3ced86794328757d21aae98d09d7dd2b33000000000000000000000000fa1b1774938bd12478a5ad0c1f727d7304a9f92b000000000000000000000000c8055859f43dff3bdc85891f0f8d770c33dcdf46000000000000000000000000bd448b5133410b2600600444d3a3fd21bb8afc468af7cd4b86c088406daf6909036dcd7ef763424df48390035d078a308232c37e75e68e1a1dad4616000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000785c6e5807c3af22e6a21a73788177673a4ed13b2bdf925125a1a01100000000287b8c3a3fcae15e2c8ffd79fb5e694a50b3684d29b46f617391766700000000afd04611bb939901d6c2d12d360d7637ad9e50120000000000000000000000007a501a602a0bd451dca41647c109094220ad2704000000000000000000000000d7285072338d7464f13c3e542b26b21abd056e6000000000000000000000000067c0d61c95c724215ede9e7b7721fd6743b322080000000000000000000000009b873d2d593a453860b69b1a089990274b757646000000000000000000000000b4081c4440c486101c23cf4b155b550011784148000000000000000000000000f7d2457e7fcdd06b865885784d77b5246a265908000000000000000000000000a48199096db7f22e4c8842056d6a052dd15f26410000000000000000000000001cd94e6a2646320512732f4b399f51081d902712368efb27a205cd0e27ed8209d839f14998df4c195dcc6f5e102c141523a4c91c2b990b19cf483c050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e2bbe06fcb1ed758335c70fbae8104bb9e19664cfbff4608a97604e000000008d4c8f4476a5a37434e3a9054443f22d6d0ab4096c2742036c168d62000000008b61716f2d8d532ec3be4d5f64dd9d5db6293d3a00000000000000000000000019caa879bd7d7925a3f3e16dc6e22369d634402b0000000000000000000000000cae4f144971372c02ccec134459c5237cd3e60d0000000000000000000000007296fd1cec1cd0754908917a29449500689c04360000000000000000000000003ae660185977d314da624b4b3c5ca81b8bdf3c6400000000000000000000000073ad4e73a422393f404b903fee82e704666d947b00000000000000000000000015e44e05199cb13ed568315d0d8c26357075b943000000000000000000000000a94bf42580acac70977ba959d6cb7179bda8b808000000000000000000000000a8b32501371584252b42185f96549824498e9112d4c8496fe97eb51775f89606fccfe900f70c4c5ab8511732dc26b4201641540c815a2553efd5eb36000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000119cf73076f4062937263b1696844955296b7a2b2e28c53ddb68064200000000e8df4c164c97716ca8832f3b818965000c8c270fb29fdc24e19d967c00000000bfd2525b82563c349eb43b245e7aea705d87e668000000000000000000000000bd6e94368eda1c7e476e01098e2005138f194062000000000000000000000000fc35ef1b3118de323f64ed4acb5c172b71a0f134000000000000000000000000abda6a587986c000b12df55b17ac5326869d76550000000000000000000000000ace3973b0ea6930286b2c01a6a36d52e4122c5000000000000000000000000087414e3a40b41c37332bd7426e206e6ab5b824600000000000000000000000005ea07801b92dbc0af758053ad0ba060bc7c93b7b00000000000000000000000039e42b521699aa5c86b8060b52fc0e691573bd4600000000000000000000000083a4ea16428fe54d728d3d23b440e33a1074814242dab5745cb89564210e997a903ef6536fe4e77449791046d6c5786ef42d39238e8abf38be70494e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0d0c500404b055d7ac8690806a1b25847af1e0993b41650bc31ed410000000020e2793dac674c4eb136c06d1e19965172097b05e50e973d72d8300a00000000fc61ed372d0e9530e3086405bc09996771c36068000000000000000000000000aa0ab2071ca6231e770c9c01561de75430a4403a0000000000000000000000003397bc3b601a3e73aa89012282fbf53e571ffb6b0000000000000000000000004c5bd32d6cb15260499d37567b37df1170a6d9380000000000000000000000008781285b8fc19327cdb667577af1a67116100c4e0000000000000000000000001c66d035e905ae4e255c8759d72f9027fa0c9f440000000000000000000000000717084063660b15b1a8005f11baae228c50e100000000000000000000000000be89ac4900c6b5089cf0f6485790bc4ef7a7956d00000000000000000000000085673d6184b4ba6466e16b7ba41a726fecbbeb3b4ecf4c70a5bf9d73de3fbb4cde8f3a6a99a2140b567c4b0df3facc2dc353320983bc4a2a193a4500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000077ab128c43c65102465f40c9d76997a68a57f333e019d3d9768d44b000000000cc1ad630087741d5231f80cb07a8318408a8f73c8c3d627b4dbc96f000000003863bc000acdb2157da3763961650961dd68e86700000000000000000000000030e6fa79abea987814a03a7c7f2ea73dd8b9375e00000000000000000000000002ad261c5659bf6b9429f75c9a28e1753fa64c740000000000000000000000004f7b6613e697c86d4062036c7c33f27cf2be881a00000000000000000000000064cd0a78a7070622726c4679ad0c5c723fb548050000000000000000000000006a959a7a30d5d00d1c6e065f9edbfc1e8b2213310000000000000000000000008d8b1f085f5f596576549d228147dd6f4c5594700000000000000000000000007c499e0d602350639aa0cb25a0137a28e60ffc1c000000000000000000000000705dc426819b2f269329fc43fc1075455790494a030cda4e074174048a9c38282e9b1a47ba1c240f52fc9c0ef588a27aa3e5d93acd982b4d0915b3730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003bebd9575bcabe742d2ce90866be2e542088095284441d67d51de10200000000974e967639a45476d36b0a66e4dfd760268444322bf3b70e8c63444d00000000f568956a1a6a2d41f7db2276a1ee3659db43bb07000000000000000000000000147f6b49425e325f876c4b61e18c246bdec0dc47000000000000000000000000b2a54c4797cd171bc33a463d69a02922a3410213000000000000000000000000cded2f271ead5b0cc423ff7247325d0064357d5f00000000000000000000000013509f79d3becb03ee4c4240a3dea3376f15b80b00000000000000000000000059701074bef6f767b04967569334696697cb3a740000000000000000000000007e24ef51b77b524809e183022966076dbe0b1b3e00000000000000000000000037b0ef1cea53175292788b43186ccc19f4ba441600000000000000000000000043fa0c48a301c26f9c1fa1059a695325edb178084807ad068bbb0f68e5846727ef8b6147c1f6ce1f12826c672e71c8047307b00a1db4e5650c8c824a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c59a20d30ab112b8d47180dfea2721fc1fb1b3208149276d588e63f0000000070bac00f0e805d103364cf486078ea4c1ea9d94e6afad42d9c819e3b000000007cfdf8149e604b78c18f7b197017fc696dba91660000000000000000000000006372b139dd38861d2401bc52ac51705def2df4520000000000000000000000007adb1753cd1ff35b5bc1a2350ede5a51ea0986310000000000000000000000001c137a54ee699817d42e9509d3ae083f6fac6c12000000000000000000000000a1f7357940660a435b1a231b3d47646836c3642f0000000000000000000000000679367ba71b36119cdda4218951c11e5119145900000000000000000000000034c00f192719d8178c483e31b567581ea0ff161c000000000000000000000000ab423f6e61899801662bd04a4f90055b2705ba6400000000000000000000000066b0703674fe0d1400650c4d7ec7d343e4737c4398d7c936e499ac11c815e545c63cf071ab62ce2f72a26c61bb444b11bbd91b0b42d869302e3b6b35000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc5cb804c699472e35e2b9714c95f553a79a520e4ee38c6e1651c03b00000000387b12767d7871124bb8057ac243bc15ccb1d44c4a97fa3e03fa2f69000000006bce1230b54f593b6872511b45ae671e7795516f0000000000000000000000002f6078125e0a2d22ec96291cc490d4413f09cf2800000000000000000000000005939917c2c8535f21b3a466a71f4e30d1f1c17d000000000000000000000000cb467a22f174e15be563c1452e67f57254e9e82200000000000000000000000027e7fa6717949634af890e3114e06542595283500000000000000000000000002a61f42088c04523a96f265a0fb5157ed52fce37000000000000000000000000187cac0daadd514b16d8c1395fb9c123ad7c62300000000000000000000000008168f335f103cc36d7b3360b98ec6a7ec893a11d000000000000000000000000dba20d695ae02e34d68e6431f174716a274da22a73435e4f512bca262933b44cdc79120e4e86e471ecdaf22386e12774441b392eaa33653b12b54d240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004316a901320ae744fc9276228eaf1b0c476bd45d3a9a8c28f2bfa0610000000055b2d522c486857c9d3fcf2b8fadb13ba2897334612d760c0913e62f00000000eed03252717bba712872a31bc6e18a0cffd7383100000000000000000000000035c2dd58cf9db078cb0ebc40da0ed407dde8db19000000000000000000000000a174ea4b572067268c236237477fdb2ba5f25638000000000000000000000000abae85709953d7757bbbd409493b540e64de3047000000000000000000000000252e685fe1044340d8cc7e70d29c760468c1f34f000000000000000000000000b492dc0f2560927c409a013efe64063bc0f1ef6f0000000000000000000000008c5a5e3ca0e57b42e802aa1fec04f0515426113e000000000000000000000000775e8718d9b2d03346d6861f4a00750148add55a0000000000000000000000001b1f195554d86b468e843c3f9171f1458aa1a648d8c00d260451493a570567200836dd7596f19a4757dfe31f88da7e745dce58521df4ca308edb451e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005cea0e3467286308f8169b742813a331ee409b5fe7335f035689a50400000000cea8d65f1a00617520ad84548488f818c19adb726d5fc054cf4423400000000042bbb7273bb85e4b3900c3199ff2014108e9c23c000000000000000000000000b5755f19e15826016da80d2bfd3093700b166657000000000000000000000000660168562629072d923a0701150c9122b232df10000000000000000000000000bfe0b56de840aa0d893b05098b53e0277c655d11000000000000000000000000b20d96773506bd6f7bce8073be9ad668112c0706000000000000000000000000ce17cf061362c20ae3ad6a66cb8eb33a0966427c000000000000000000000000c0a38412286cd060ef57e6535f4fd63537ceb449000000000000000000000000d54dc22feb5ac53cb9def10997839a3c38697d6900000000000000000000000041fcee5c451e1e7e8e6cae63b6d33852808349198280002878070365ce34787e64937c16f31303373650e16a4ac7616edd0a873b5512743310799d45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5618132b902692f8babde6078bc39425e64b37d7393114753252a3600000000146c405ec137aa5d3766a51cee63e44e4adde84bfd3cf763fec03223000000002e958f647c74427837715e31b6427b4aff044b420000000000000000000000009cdf2740453a5d7673f58e7707857007f700b21d000000000000000000000000da648f5cfa2b8b533da90230b8d50d733a3e4750000000000000000000000000fafaf376a3f1b6785d681f18632b5e37287a7065000000000000000000000000119a305a1eae664fde78965e6113284616e381100000000000000000000000004d43a5114b1975093c900e5be18c2a03766f1055000000000000000000000000fa259b29e19c4c628783da176482fd108192383b00000000000000000000000045403638ba554a15be8aad02c49466569ce108070000000000000000000000008d62116bc0f51e16dd0e8638392e835137d4da579f8454333e76f330767351224830d81f2900d93a76146a44e97536452fd53349a4426810df7ffe5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd093c63527cbb4a0d5adc031eb5ae65fc76fa2bc14e9c4dbf74950500000000612cf54f292e31075948930b0ffdf266965fbf2150370c79ee7215360000000052b15b5100b2c90162ed6006c458f35d02d4a554000000000000000000000000587c7d58d35c08673d3eca5ce39ae767923e36670000000000000000000000000376b23f405bfb37c8339419163c523d1b7e6a72000000000000000000000000c3cbc544a0fafe1619e83075d40b134345e7af60000000000000000000000000f5fe687ef862605da15fac018152c30877e5fd060000000000000000000000001bf3766aa7a0a02a904d1364d756b1790bbb8d590000000000000000000000004674920ea87fa65bf02cf51180d92a4ccccf8e2d000000000000000000000000d18c6f6d4a23805bccf39965030d4e53da72010d0000000000000000000000006817524bc52fca1ccb651440a482082209088b4cd3be82349d2dd639290a8c559dde693680d6283b914de72757b7e537bf7df305fc09d323051f602f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036004b7e5e439650b8f2d81a613f1f57fd349836125ac91f3389a5610000000045d6c16db896501d860ec714cdcb773b39c45b0dc68e9c565102522800000000d8613a6c64524d5cfd79ba5c0a30104ef04be42f000000000000000000000000fbf0e86dd50f114a69737f7e8f8225616426bf300000000000000000000000007bf72543a935ec1615e9565e6629942952c8f0550000000000000000000000003fb1130b2c86db181c04d737f48ac621d545b74a0000000000000000000000001b297f6ddf7de14ff3381c75eabfbf53301af05c00000000000000000000000015d1290ad7ec722452ebb60a40c30e065d5b921300000000000000000000000075c47102d9cb7240b775cf03d3938f2074bb1800000000000000000000000000dee9b97039df11639a76ca1fbce4db1b9b1ffb5a000000000000000000000000069d6978312e1d008c28c7731068b8222e1ebb5e633bed74bf057d3595d5ee5704974a3ae8020f2d4bf4921f6203f52550b5d06fbd944f354242c90d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022b555472965b8620bc995413741db558fd1ac61bc5a31603ea20c3f00000000b4acc42384fa0e533405192aa38c486148e163758442d410443f792e00000000fa21ca765eb54162fcff366dbf1a9074ae40b55e0000000000000000000000006ab26c79ed1d244a948fa931fefd9328e794cc65000000000000000000000000656fdc673d291c0d12d176375aa40a0a8fb26656000000000000000000000000536e4a75fd841a5c0031c21d8acfec07ccf36d530000000000000000000000000f12ae37bc99827280d6923e262231476e55a746000000000000000000000000c2f4fe1412ee5150293b5575c7655538f13d4a450000000000000000000000001dd6d617bd5e8878872b354eab75c01cf6f60c260000000000000000000000008cafc768e4248f328d24f869c046525ed234836b0000000000000000000000002a663d14f5471c4730fed65d2f388c0c96536f6f957a732e5e52c166c25e8f0f056faa337758466839095c5989445868b7e1d6776440f760b2d689130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000998b352f6706e5650ca8e30c3098a6421db050c0482933c7218363a000000004cd1cd406431392c6cfb4413b2a78833745ce6244c8f451af952e67800000000e66d401ad9fe1a2626d8007b30437f56930ec05800000000000000000000000008a9717eb0f9547098bae47dda27de236b055b4f00000000000000000000000097a40a313799da050c9b6f4b12d66253ab50c62d00000000000000000000000045b51c76e4a2d822bdf7d74e48f6a405e33777180000000000000000000000003151cc1be47beb38fd9f331f2b9dad1f8979756b000000000000000000000000e00dc54551517043c6f16826e8f00e3d83263b090000000000000000000000008a46e3193aa768685dbac828d717216871c2054e0000000000000000000000003604ec74810a066c3c9cd27a75dd5465d4c9a40e0000000000000000000000004610a147c293763d55ed664058a22d729e5c61335eef6e242357897ef23d456acdd2580ac73a7e6e557bac0512fd6e5404660d560184c1598e52e277000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c351de196530f017fa2e21362df7c54d4d852116738e4d2363dc13740000000084b4694efce29221235f0f23a985202dee45ed648e2bce53b6c41647000000004717bc4a3fa7572359d985589d800f508cb05f2a0000000000000000000000008e604c0238f5d53202493d1e8312ab73bcce210a000000000000000000000000195abb16f4bd1f63a8bb6558651e201ca931b240000000000000000000000000d7284a56fe82c27aaa0ba43403584544e2b0d26600000000000000000000000055709a2f8f20ca531647192139fb3d651b2c7c56000000000000000000000000622f2b63114b396eece9c93ee8fb1f69f5ea194c0000000000000000000000006b32fe755644d42208047156ec531f2a362442530000000000000000000000003d0f9306a63ecc7a5fecd1782bff29093543ee5c000000000000000000000000118405279a15775372bd8e3a18d7101bf893d768feac77042e77a8438d92d9103d4b444196d67d540959aa00a9895724b6ca7e1e76b46b2c9b82cc04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dd59fa633dacab6d1d5fc470050cf654f05eb30c6ad4b777de95094400000000813e0458e13bd440b2726a03ff69636eadaf931d5984d1474084145b00000000b8b72240614f944d8591d513a4d3152364d1cf1600000000000000000000000034266c0e6f712c4dfe28667a2edbd0619fb2146f0000000000000000000000004cede00fcf885b5605cb0330a73f8d11af56861300000000000000000000000070fb4f0613f4843407bba04ac95e75490094111600000000000000000000000070d0000a61fa43411505431d6cd814046a9e1a74000000000000000000000000724c0a4450d41450cb54ff22a941467d624167620000000000000000000000000607721c99c61659d1289409b829f86c6ba3d214000000000000000000000000bbedca2a9985cc0544d9fb5446f97d5a8a9b4a2f000000000000000000000000791358559396ae7e7971a91da27b455d80b133514aab966fdfab614fad12763740d77a062da2b65c713469183f9fa75cf4ab3c180155784a1b2cd434000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e90e9009cc77a94fac2c93489fd1866cb65ecf771d521f49ce2d176100000000adaea10f2bd75d2accf3e36559f9bb200697746cec6c7e6708b1f44700000000fee5cd57f4b9bf34af40074c6c807f2d26b1627b0000000000000000000000008a6cd9334f7852518b4cf51d1e85e242fdc002470000000000000000000000002eeac6721699d30e818d2b10eef7776bc6223174000000000000000000000000bc0892498d1b177913530e49cef85d1b938eed6400000000000000000000000004577c38fad42d4de2eb87500cb980637f4d4823000000000000000000000000137cd96cade99024e084045d81a2982185b2001f000000000000000000000000f73b7f41aff391455b03c2328cc8202744aa785800000000000000000000000095ab643d233444007d50b76cf4e5130e43d43832000000000000000000000000017ca1095f4ca5719e0fd97de2497812dca61503bf93d75df7d5e553b63c167754eca11823b2bb5458e2d47bf1cd673db2bd290c3919491fda18ab1a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a1dfe82455919127fa4f08471bb2241061017e5e181ae61158da7b78000000008366fd2f98a3d818305b1228d039f475665b9f25a01f2f5b38701e2600000000346cd247a3b2f3187915b77955fddf03b86eea210000000000000000000000003d20a855f5158a35e1c7062fd89f214b4920ec3d00000000000000000000000013824b3d6f7d2e69698b9c6c06f1b77345b17070000000000000000000000000990f465773405f1f8873e974a22cef0b964c8e790000000000000000000000001c048c704841bf19c955a737632c9b4ba7edb01a000000000000000000000000be2a76691e58ec5339cd2e2b1a216c601ec43271000000000000000000000000697098102d9a871e34fe964392cfd829fb00035d0000000000000000000000008999f26dc8097d65f8d9666de238024bdeab8b0c00000000000000000000000077757776dae0e801d55fb61c59123c27cfc93c6713e11d691244c4677becb009e506750ec78a5e66547893336eb74c50f18c184a667136451042922e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052507d6c19909b4d7e1e256684cf7f54bde350053658fa1210067f66000000003293f4720b16d1648e0cd93a183aec04ad6aaa1c70eea13242d82865000000006823fa14aa83791948a5a0031fac581dd1044911000000000000000000000000b05b6930794c9f74056d64409955727e2fd50647000000000000000000000000c637252f70a35266e69a883d2c076060236556470000000000000000000000004b76ff76023b014c432a1d6b9f642d47812d686900000000000000000000000051b9dd50376a9d1f44d3c10144bc514e6541ea2a000000000000000000000000fc7bba19315f45553c970a04237ce86158d98536000000000000000000000000772a0f6c0f1a0a5e30703543d5994a3954c3b86b000000000000000000000000418cf748dc8d0c287405686b721b4b0821d2892200000000000000000000000007a9975cf311d5364cfb4a5cee72634e649c2140b6777b46e98e986191006b669a0d5b567fb40b74373db947926fac138208301eb84b950c1f303d3300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023095339e0b6740742e6df526dea4944c41d9671bdb0690b119f1303000000000bef0635e4d18007bdd4b52ed6215e282a0f6c45c3badf2f70882303000000004882a97aa9e68277a28f801800ade54632321a0200000000000000000000000053ff18677c693e0e3913175c537c12794e691a76000000000000000000000000c1a8104194abef0df303b72c89e09c66e70a191500000000000000000000000090fd363ab422c07decbab71e70dd6418b982a8190000000000000000000000006b311f08ecb14e71857d7f1adc6b2f68f62f5874000000000000000000000000cdbb656a16ade743041c2e6f3c936460bbcc8e39000000000000000000000000e2e0536cb10940291141fc40e9831b00a68c6411000000000000000000000000a531a4751c6d6e50d981e63ef1687b4f1f4280140000000000000000000000007c26a15f28f8c11820ce426daeba7248b59a06251b5be369aedf240049fb3c179bb36a2b13635b6cb825f374df7b6569919fb620247cb11f3431ba47000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a785a85a2a097a25e342512c95f716171d8b5372432b8c1def08276800000000bbf29b2a90b2ed1af73e4b5f1df1470d00fe21559ca814721d2a601600000000e7e8d92aaea66175a3619c404443e105d995ce7b0000000000000000000000009b42f83fad13e93338bcf144e0b2cc5f8863ef440000000000000000000000003832175da5e0296ef88964537d500f6a99e4b938000000000000000000000000725b010d8434e54329992a70384cf6286152ea280000000000000000000000000682311c7f027b1855471658af6b454b4ad7040d00000000000000000000000008c97c463066694bdd07bc559de76f3d035cdd7b0000000000000000000000000e60e554637917457e9b303a46ca2a4cda711e5900000000000000000000000042f39a55d22c4801b688455998a24c4eb862c93b0000000000000000000000004974426c262837384ff9b8797fec9e3f9d695753e1db1a57a47d8e1619ff965c65d11232b442c5726377fc2fdca22e4e890245409d66ee1bea3a860a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011accf3993b85f2f9c88fc74b894536db367a16cae661f088ff79b21000000008f7a5a3a7cdd7717c681f86dbc01860b224f587da766285a7664db39000000006f40f5083c1c500d1cfb8e78b03cda5f0af70c09000000000000000000000000f55ec02145b75b48b81a534f22ff7e62c26610080000000000000000000000007511c41495f9e75eae526f64ad1c234691d7de780000000000000000000000006fa10f4c6fb5c85ce7a6e8695a0a7429db7a000a000000000000000000000000427b4075413e8f0b99d5fc0113dba76ad1c2d80d000000000000000000000000afbd64523493440bf496932283c3a605df8ee0410000000000000000000000009fdf865d48fe5a3ddb7fde01438c6e7b354431020000000000000000000000003ae84c04636cd057b02207397862914db7310330000000000000000000000000c3e803574f2f4362a6d6fb401399e15c4c36943a92f86d091c1712461b270277ee85d24ae0e7f676e517d37e7ed4d46f5836b21dd6ad0e5ae045f8420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c68ad64435a421b9d4aee73358a3277044537388bc8c624e0334d6a000000004fc2187a76644d1e4a3704470c0ecb1462da1a63683bdb51b0914c410000000093da1669187f7d4c9adac2255e937802ebb9020f000000000000000000000000265ddf4a8d509b15f3e1d90a674b8f2f8fd0cb56000000000000000000000000fc975716daa716438eaa7737d5cd431d3a8d82340000000000000000000000001390095c4141a03e9808a060b9f189767a325306000000000000000000000000155a482d33577614bc4c523bda4dfc2955486b7300000000000000000000000037841b472bab6f4c88e0cf6987661b271f42561a000000000000000000000000cea7e7436740da3a4218c60c0f491c0c24844d1e000000000000000000000000b154126071e79d23da758809a3fa173f0599355c000000000000000000000000dead4f6e72333c6af4f4d6520fcf852f2a7c0d2a87eea5656a5d873b64a286414992fb1eca38005877237b38a9514c22a435b12a2daf7218670cb473000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c7f0164733f70c41c536c028c35c7c46c9170115ad94535794a5567700000000c076f81a9427934cec58342025c44923d1de58567361047bde0f4a7e0000000024e1b642e8a737787bfb0315b471fd5d7a323012000000000000000000000000d33dd44c6505b306cbfd71164963d14f076f3e71000000000000000000000000702d3b4ee057133a2330ec5dd921c5053b429053000000000000000000000000993f3e592c0d00676878775b95e2480f228d5a1e000000000000000000000000650d83539f16732f532c317c47bb092b1ea6bb48000000000000000000000000ccb80903f55d951b09d5c4029f7c9e4b27a7f85a0000000000000000000000009ccf8f23be7fd07cf128d01ae5a8735b3f9da14000000000000000000000000046b23c755bfadc3cb22c8840983ba71325d76d4b000000000000000000000000b1512f70ee65192cd4fa220c50bc6017c9c20f3fb253c24eb6a9bd6c797ae857127bc66a08a3dd7e8c90d33ed9cc171595f1b641a6f01e63f384ab0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d57665bb45ea6152a9ab718fefdc01a7b56665c0b65004b66b3af4b000000002bbb006e75122779aa732e7e52a99d3b1ce4ce702008892dd8f09e4c00000000e16b156ffdc6083c0e9e6f4ac58b54787e5dc16c00000000000000000000000050084e2d8942997626416f70dc2664089dd5e370000000000000000000000000917a40788410881f327cf526a632d133430a4a51000000000000000000000000acf2be68c9447448662c34396bc9e70ca5894f74000000000000000000000000c729fd65cebddf73c183d1757088bd56b110f450000000000000000000000000b69aae295392294893af0a5c3daaf26933dd6b54000000000000000000000000a759463fc98a941d0103df1139654c2b24449e7c000000000000000000000000f6b92633f2c0bb4107890f614b24f003f9fb402a000000000000000000000000c19bea3feabe1b3e10e49a3cc7a2ea636737ef1ddf5bd851745c926689c7ef66ef9422704cb2cd3f56a6fe633fedd0718e465d569af8a9374e79cc4100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013657347a8b57e169b4f1c29cf22b57da9dbdd48aefc747bd9b85b1e0000000072d151310278190ab0ecdb026389a816adb9251bc53fa018b3c69f6100000000eb8b3a7be3767c56aff03c5096a17f1c2247871d00000000000000000000000042da3010c4eff15bfbfcd30fa7a00425bdf29822000000000000000000000000f5d9150dd062b46fc695c375598b3969bccf1b55000000000000000000000000dba38e1a0856fc645508ed2e411a1921d29fc02b0000000000000000000000001872bf2298ca614244d8db770683fb0e29b0196b0000000000000000000000004d64ae24ff2fae7066b67c03a0324a7bab48b10a000000000000000000000000895d2c625ed0f63270435449ee8686330dcaec550000000000000000000000007ad8a13b5605e263ebab361247be9b7c477b7c16000000000000000000000000d2471b2debead554ee9421668faf6f46130b61551380531fdab2b21f87382b387aeef00755cab46054d6036efd14e6645ad232691435473dc6237c4b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c966521e43e7af3fc2644a23d77b093c8a4be470d4373223a86f4b6d000000005fd0c75335a6c1547e00f14a911fd205f09287003787410c8d3b9d190000000056432f6f05f2e76c52b76539d4f051638a3ad35d000000000000000000000000807c31744f54a9095597725602292b49dd05120e000000000000000000000000eaddac156fc649352f464e56047c6522fb26ee1800000000000000000000000046c62914d8abc03a5d09f768656a0521ae80b47400000000000000000000000069b7af106dd8987b95019121f5a12f21087db77800000000000000000000000074b9c833d4fd7a3d0d81476e44b8725bed0919540000000000000000000000000d0bcb01829c5059670cf2016b74fe42df0ebe2d000000000000000000000000faffe349fde3691d07d4e12d38e88b74b7b64f6e0000000000000000000000005c0d3c5ccbf7723c16d209179630cf3a41975651e823af549efe427697b4294500beb6434ddb0e0620b4bd2e2531fb65fa3fbf144ef8cb375d35525d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000071f1b3011f97cb6990d76f038641a5009f7b9a26a6bc0066cec78b630000000078d322343cffdb6852f2c90a6bbd275633003f2a7f199616d7a3c4560000000055979271dbb51303220be763d978aa6c96dea94e000000000000000000000000b0e1025d0b13483e93f0e17ee8776957af3627030000000000000000000000001c7bfe2a04bbf07896ec170d06ce990efd461f7e000000000000000000000000977f3d3382e8832489355b5111008f491630a70c000000000000000000000000519a8e42f17a99177205e474b555ad7ede4ca61d000000000000000000000000f531195473896033ff48795c30ca9167b094875a00000000000000000000000070f61c0f8a8cfa2d3514ac3db57ac822c561ef3100000000000000000000000001b8853ff9f7b339824262716666fb1fc461cf470000000000000000000000009dd69626dcb17c11b426243cab8e3a4a69e14114ba0ecf150dffbb056eba712246642f1765b37451ac0ac74869a8bc5534802975f15ad705e34f860300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002688871beb04267fcb3f56bdae4693fe977dd2a3761e860210c9a1900000000dc38a314cf9c400dfa130751afb04e4fbd455b21164e1b068f2bec4000000000528f6c5d646ad465ccc74e771f1ca732bb72b11e0000000000000000000000006105511a9e0249102bdebb0ae8564379e01c0e2400000000000000000000000057c9ce1b4569af3d46b38e75b0705c04cf40066e000000000000000000000000fe027d1561d96d6dc366654be1442c46029b353c000000000000000000000000c01ae60fdb0d1e171a229b5cd590bb08711d1d7c000000000000000000000000c0e03a3fbd4ffd1f5827eb267aa5b5463b32490f000000000000000000000000aa43ac45ee3a172611ac366f720144039c948c7a000000000000000000000000165580784ccbae37f2c66f004694042081cbad150000000000000000000000005c932513bd07e8043d4b9118cba1c25cc674c67a87a17064e6af056c5896c257b14ab50781a3497e94f47556de19ce06978846211512dd2461a9a17d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a65eb310b0cde12cba775166ea2e01336d120e53b398945ddf4f426500000000a2714326c54e29715df02131056004569dca8b2b3a42fd0ef046a61f000000007c1db56f88e25168988242466ef7540b879ba84e0000000000000000000000000edfaa3eab721c4ef94f442caaca9454d85a407300000000000000000000000081443d7b4351ca785830541b6a8a013fc9551f1e0000000000000000000000000fc4116714e7124956f8b618a689d95d805f096a000000000000000000000000730ec36b8477011d11f4ee2937c0a561535df8010000000000000000000000006047ce6ea9da835044f8a254c9906b181fca4b4100000000000000000000000094a17950bfb7ac2ef682296d804b33452d57110e000000000000000000000000835ebb4730be761cc755270e454f256dc767607c0000000000000000000000006c376e00002670188dae9e2616261623e51aef4d48f5b048845ef67156302e7d5e930f70440be00f854a59417d9847102032dd5594da1175186d72180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006551a128eab6e057fdb2b706793d072696698b1f69fabf1c5e06a25f000000005c1a8b41bb94aa29998772046acdb761e92e8b4bde57e3327539994f00000000170f644025dfc3082ec34e4c08ffe531f35586680000000000000000000000008e42944b0532a56abc22974f9ead8734f8e54671000000000000000000000000f0ad98649bef720010378128b4974c4d60448a620000000000000000000000003d68722e63bb3c0e9a4b07035ca64d4605783b6b00000000000000000000000068810a5a035102036f8fc633ea4e084002e3b22b00000000000000000000000026117b615ec3f0141d665002cad1e16971ff4b0c00000000000000000000000090566d0a08ad206641ce340208f2b901df099d1f000000000000000000000000122a311803bcf178494aff5b02ca071885f8043400000000000000000000000057eb851eb115a15f6acdb210d8222a00ae97ef497d8f3a06f0db6e3203db4f1442436836ec64495bfa20fd293eac431a9915da4d4fa083072d7c0b58000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000859a823ac76a255a6c220233804c6d45077b4716d968674dd4c9292d00000000d0a60b3e09cccb2ca9ca7a0ad87a9765015a2939cfcd043818d3221e000000006118c51a18ca1b423642f66cd33f740c083550230000000000000000000000002fdc9d4b6cbed43d981b3b5103ac8e0e43847806000000000000000000000000c611705d32f3a95b98186c4c82a9ec2ce93d91590000000000000000000000005e620e0bc996dc471857c273f6669b2e5ad516210000000000000000000000009ced1e13ca1fbe6203bdeb29ad94f54c082f107d000000000000000000000000781e7f3d502c133cad0f8966742ed2279917424800000000000000000000000005dd7848a733162eb6f42260707d1c4956d50243000000000000000000000000e49def65c3fb2c739d3bfd0f0b484555ba0b9623000000000000000000000000290e1314fd5e5f3458f0251010854d03424e83784b611725b71eba227f4c9957ddc6691a8624730aa5242b59739cfb3c5e043b261efd1159cc929412000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000726f6f4a9c0a613ec05b847e3963234a032213447cca84578eb60a3800000000258a8014f406ae62d817032006e97577541f502bc2fe0b20f022c57a000000000d9a0c7e95e9280869ca58426f68f261d78a8148000000000000000000000000a6dd116900c1fa3e4c63c60f61b53f3c27a24128000000000000000000000000fd19c235688a6a6006ca2443f6b8995e7504091a0000000000000000000000004987192b628b0e66a81c0f7cc974c66d86fa3a6b0000000000000000000000009332a91e436e3c2d3c94231b5b85913fc26861500000000000000000000000004055965d8eb23b57fb6aa047a706031f1e884324000000000000000000000000f91e443ce96c6b6b26f7c703f0addc5efa97d5650000000000000000000000000c9a4b5c140f11199beb7e6c956ae11c5d51707d0000000000000000000000008a141b4d96b38d05ff0ffc7db24c464dadd74005f61f9262628ad105881f0e35c6212634e62d33049fbabd6383622231c72e997b45e42329fa54f54b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011af5207bdf06a15715de963ad7cc651a7aa36257610894bc4ed4a5900000000212b2d1ab287554a471dc865bb66302e0bf7381de6956d287cc6057a000000005ad40c550a57562ada8e3357285fe41260bfaf51000000000000000000000000541c9f16f730cf079095f350b9a03230fcfbeb41000000000000000000000000f5a3293d40aedd7744994b391046390f00ab9d5b000000000000000000000000ad2f5437724c1a6c329b494f4de78f1467fc6537000000000000000000000000a490d317ab5f4356c73c66746c93961a1297fb7600000000000000000000000019d8890d04b84c633e94342592be1b46574f22280000000000000000000000001db41b672c00d4124a93667b5143b6254d5f2869000000000000000000000000595f506c678348598397e559302d2b026e7a4630000000000000000000000000bbf4a540d1aaef754def5b2ca5a1cc39b16b9446b07eec7c8355b45334c97031de5b307bef2bbc06df9fce10c0becd71a82e5d3a1e98fa4ef48a9557000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000388b5e269983075c353435767bf68361e6059f1e1eff0e012d24625e0000000087cb43357978471b254c8a6ccceefd6d47d7b178e27ba050bd8c985d00000000a1ab982ded55b8202273bb6f1598b761dd065c0e000000000000000000000000b7c3ea5d39bf825b90d850271073864b62609e1800000000000000000000000013299f179ed2953246e3d20324bd453837224b33000000000000000000000000dc2e970696e7c01498a82f3c3a718d4f09946962000000000000000000000000904545458c58647532511524d656266d95cde50a000000000000000000000000a1486703bdf99f277c0a9846a1485a53272a9742000000000000000000000000f64f5a5a9d932530ed69114a9109d207d35adb7e000000000000000000000000156c754202441822e23dee709c34aa4b98b32c2700000000000000000000000076cca3341853eb5ef3d5707dd1f102038890bd078e437e729a3546173c62d01abc35596274526d27ef97045a297afb1e42a45822744f5116dc9160550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001662681624acd0726018f062b4d3ba45974d0d426a57b64e5a268613000000007ebbc0577be5281ceed3c54636c5180f889f6d10c33953052376843f00000000077e000f6bd2f14199d6ff286e28732f663e7760000000000000000000000000f700a939c4102d4d77e5330342248f17287a1e1500000000000000000000000040f9b25c8a2c651e47dae179c5063f665c8eb8300000000000000000000000008592e75ca617f946e794ee3e3ef9fb3deda1244700000000000000000000000093a45312c6123a240d92ff3886e7142548f2a9050000000000000000000000006dc6a4334143d540de6e5b40af189d0b15a6477900000000000000000000000031322235ac1f714a22c4da697b6fe83bf5e1aa6200000000000000000000000037804555bfef8e78dea5e24296cee436fcc4c278000000000000000000000000d3853d3ce748a41cceae1a3720221e07d04a851725ee463fe936cf138d4692320180220a7461c758017d9b768c9ca11edaa20525e2106d51016bc9630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002218024fd251f84c7b4a64369255eb23abaec12f0d56c53d6ba06c6f000000008979e75e73ba3e515d6df8432891443c8c08911a7d09a75fd40fd32e00000000a3fa687850c7564416aaee00767c9a2a096a297d0000000000000000000000002fb3fb70ea19df61995cfe1b29245e15c4ef900b0000000000000000000000008ee5fc36f8174235fd75a4193af07274ef82f6390000000000000000000000004090002a23e52e6ad6fd6547c99c603e255b737a000000000000000000000000fa7fed0fd14f070f8e71523c99d3dc100b1009620000000000000000000000000198987cdfccdc26d6c3196ecdb2dd69010d782f000000000000000000000000ef7ce46681d4355a1b99f32d2a21d528b6316b61000000000000000000000000b725e72cd922613ae8fae175c1c53c5764864d4600000000000000000000000006d357013fa146439bcd8050b845034f2b2ad906d52cc60e84d8b71eaf746911cb54201ea0bf7e674571e67b23aece09cbe7a72f08afb805bf19876b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2c2ff721c2172253da3383763b5123ef9226773ae725d5eb6b770460000000081dcad56b298770e2ef33355e30c3927e2b6667eb8e61079943de47600000000c9971a4b1575f624b848452de7e9011dde5e347500000000000000000000000006e3fd63ea48f471dd25735b48481a3db83d262a000000000000000000000000fe24681b925f1b6219ab8443061f4245a59f15280000000000000000000000003636831285175b332f0b4f78eb099e22760d3468000000000000000000000000974e456d4dc3151ddd11f4707a254962f12b143d0000000000000000000000004d82e4131dd8cd2f09447073626d6a612631f626000000000000000000000000f7d81f5133b296001acc3405373e2b77a05a073600000000000000000000000012d49c67ef09641a79cfe41e13532e49f2f2d243000000000000000000000000ba73683d3aeeab083834e015c3a2d04ba7ae8876d3a66634ea70522bb22c724c9ded353ddab1a85ee3b1043b1fc4bc59cdb97a048d9ff60e97f01945000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cade461e357fb04d42918d1dd671e23bf53b7f169ee4190095925c3c0000000024783f7aa922a778ad2001339097201cb78c395a502973353756dd2300000000e97c076ffb983c06d09c1d29936acc117522a81a00000000000000000000000007ae2d0b955075629f2acb08e3b07b188a569e0a00000000000000000000000074978d543d80961540bce72e505c5f0c5af3440c0000000000000000000000002696f9758eb94a6ce59bf305378de40d8b5d7004000000000000000000000000a28d9820a084986b6adcec68319a30106e3c20530000000000000000000000009936e3639c52e26fa074ea6b726e050b30a8a0120000000000000000000000000a465548d404156c672c9b3bee63f90b04cbf37c0000000000000000000000003a2c9349dab0507448c4123209dcb71b92ae211800000000000000000000000061005a4f3f05ed7b2fa5531b1bea0e5592f80032066390035a3a37198fdcf54bd5efb93648dd9274c92662205ef6f71d01c4e05879b99c625659d23d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099bcbe3547b0074893787e224a81cf70668d4c35d94a495aadce113e0000000023babd09428b886637f0a6243887314a9a9f363fde45c805ee29190b00000000c9e1e74ad36db87e46bdcd0c9fa5ed62a7cd276800000000000000000000000044a9565913652131c7dd094d2042022e29f7ac750000000000000000000000006750b4282a16307bdb05035aa7b6940b2a27cb4600000000000000000000000079899540b346a41c784eb634774d891efff627640000000000000000000000004def5762942fa73dbd6d793a653cac0cde479d5a0000000000000000000000000ad7752de694313f35ae2139f6193460922c83430000000000000000000000004b9b7b38bce38d5f341bbb4d034c0a4bb12d2846000000000000000000000000577b242083c1e00a27beeb70a0cf493c45a1c76a000000000000000000000000fef5563db11a636550cb580b66dfca4393c4f96c5d11ca75bb60f07b10c7f32707ebfc0acc95a6223a1ef46d81dd0748f5fc7f1c7bdc33454abc275500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067c9c8245822740659d32460d28eef6048bd62551bca5921f2deb154000000009910e821ff547d5a4745757a3386dd22f0866a5c153b435ec62dba6f000000006bd4d072de3ff30cbbd86f203df8db4d5a408605000000000000000000000000f5b91136ab666b7bfec11338a1ba07323d376f440000000000000000000000001df1992750bc7d41f951d6356d171426f310c841000000000000000000000000128e00331341e4699767012cbc3af20ff1fb2f71000000000000000000000000f6f06f1074d4d0237130bc68a30d01158b69fb110000000000000000000000004ade2a31525b9005fe4eca206b829120a3e045170000000000000000000000005b545a074526b8390f8a5e0eb2869a2bc9eb7378000000000000000000000000e1915772bbad3f555cc8583f1acbaa32ba8c36630000000000000000000000000ed9b56e96ea7c74c382cc4af04bf614b4f60169a5edd8436968565053a8a82bd7a6dd331606273ebf54f3240f6b4b45bab0994ec059d05700c12225000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea67bb5a0b8b252e20e7ab34516450173678717cd974ed43d80b4212c4ff6f6108d69e788e5a1a6bae07af2a32c8be1d93fe727ec524d0565a03443058fc502c9b9566164afaa9648244841c1f6df50323b5b31115580e7a69e15a3c3d2a2376335cd55ed9d9c6382d3032098bd46c7e2efcdf5d5eff7747e3cb9a1e490030335186f849a2fb681fbf1f8c694a991a2ec169f3058f87966c503f1d4af3eee020df69a34566a0ec40cd710b7011ee201fdf0ee27a536b1211a5bffa18a3db9a7e1351582826576625fe54d5308160b252ad150c680d575e35360d154ad8b198178c29466582d8cb74da299265df222e3192d69a42fa3548722e89c256fdfc757cfa0051665cbfbe0b72e861535beb4e352c5b300b1d5cfb6756900a396da621419415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3adffc592d5367bf7cdfd33e174f77b4107aab9a47466dd9211bc58b6eafacd15ca89af807100dbd648a568a60649b6d022385a228a1e381395a4a203cb0360e2dd5e80e69c13c501166c6df002a44ad006d33076c57b2b57362e88f68471e9b43549d7935d3097e4767d01042c290564dcab9d504e2a1fc263cc5d1423c208e1967654250a517a750c423d57ca226bc72ff53615dcacdce42da62c547dce5a23c260f097dd3f64948d812ee4317a2570bf60082183df567494c4fb05156ebed3827b44122b97b836c0b6ea86343a0b85f7a871010f7b4387ee556755f371d90354c2b5323aa67eb1d560efa0fd1b37a5aa93fdf1297ce9f4d8d753b045ee77f0451f6b4392b1fa628cdf08f4631e7ed4ac01b690963369a43f1ea9c12ee3c2116dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a502b05571e92fbe46ae1ed731a21b37759bb23f87667026660da16dd071575ff06e2d20168f1271e46bf35a74c46202108bc7e645d6db365060a307a4ae1003d20dd2f484e6e6e2d663d16c14def8f6a76a2ab78452e24cd4eb2a29338b608a475f271f327e31f1034da303356cf1bfb17a768dc13f1cfd37ab5412412bfc7663a5335405ae5dfe54d3ed3b9575696775c8ae07d0d2468fa53581b5254acc3620699c16b3287c18f0d09a66a6fee1d0409351cdd3582097a4b9eea6600bc34d90f6625281e0ac19324f822fa3a9b410c246953b55b636e741686d66a402b677a42f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a791d715fe591767205b5375294a6ca2575d55f16bbd27439c54af023cde546175184686f5ffa27025da7827c95b0d2089c26976033110630e28fb153f91eae1d0fbec4462f3f0231a92c874b1a7bb80366cf0838ce9b8d0e40051d4598bccc5ea1b68634e1cf5943969a223653fb3c215be7aa7e5af0ef6a9c1b406830c32526e1ccbd62c7035a4418cf2f5a1bcd7a10dcb879771b6a226b4d9e1e1db0814a166eed6b03efc5c15137d2192358edf1117a301938eb521f5d060bfb652d8bf55f8bbe714fdf7fc12b95b1144cfc47146148fdf263cf3887070d2c3a3352d101387b45f343f5dd302ac49976780a87d321b2c1af6d9e3a175a81848a48afa2602afc9f355f5dbab711efdb7e74074ab91d5c45674e90939f762b2df2706a5ba52e19320c5c0d3c0075150f1631df546e3f34589130592724425836a16c4947037757c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5039d21f393a24b21ec78e70754ee3e15a533cfa34fec55627459e325aba953d1395f0b044d001562fa72437296b3b1966b91c0770d44e88716156de053aa6e02109a0b13c84d9b937f7584c6564861f4c29666e43f83ffd65bec69b7bf91e141d3ae9bb40149dc57464f1ce3a6e04d401d4b2284a7d322e53a4e5b55df9982077b10fba002be44f71fe8e922ee135121dfc39a400af90452a3f852a606010965fe1a80d3b394ca52f3f8b7365e7429911b7821d760f63c1725f055e5391d8c216b003833e16da0c677bc6340892a1c264c75c6f62ef0bbf4bde3c102d43cf4104ef53db265aafed23b7e40a614abb1b072672b5778dbe5e6eb551a46aa33b0b021c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a6b47d6c1043d36ea9c5616223cff1297dbce277777f36424f42406e54e0aa0322384b60937755296fd0ba71ecb17874b1918e7b28f16a736b66510a631d352b31b10c779924ec6800d82340daf42c74409233077aa4db19425d614cfdf4d5281c1c541d9c46b12b29a79d7dcd98231d8be10946dd73a7029b3bc51f33ef8e139161761ce6cff47afff0280d7591890d5c6bb7151e1c6a1c1d76cf17e806a750f830e0157413a047d4a0e204eeac024eb1fdfb08b88c2926844a8666e6da3136e0f56f2c861788052498726fd6a4bd3a51f2601a62fc1364c7d36f065158f97c7d08f65787d9d6381126c207e43c5e22a2b86376d1fa096bae03f343e1ddb513b1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af688da7571bdac4749d4417759a6032b9de9761a55e6cf35d4176f60dff1953992f2d04e3beb66389696ae02081f7e555fae3c05f16bed0a5221ce156aa5c73a2d68325b304bca4a475a0a5c2e9032532a7313342b54b432aacdce44823e276d0033ee432bbe907414ebf76e992f2f624e1a035353fdd43fafc63826e28bb03552804d7ad8535b7453fd122912d08e3f643f5d58d1c31a4ecfa14a4dd5175c479fe6443c22ea8a7041d53434638fd251477d56782ba10b577114870340bd2014bbe0142010bcae65c2a160016dfcb03b79c1543b2d6c04349b344a643233a91b92b0a334ee7cb94179b7bc24583ddf753973712e20dbe604ddf8df155aa3960304f8d362cd0fc412a749c15813b37e3a08f91c51302fe449ebcc136214369c2140d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505a3513760956db4ff6a9085c4e96be14cd07824fb648b95027c15202e3422146234d40668bcdd07529bbd9609d170f10b3827b5cac4a1734459c2b41fd7e9e3f793389318dc109799a952f4f53655b0b58ba49076d850e249fd29e13a44ef873cee174653c4a7e7361e1524f923c2409f7f9705dd0ebf1390758bc3d03ce266375ba044f22bace711df0982adedd31502d148b7ab07d027b29f26e57acc0321bfa68a62a7358b834afdf251adbfb1706457d3765f36a7934cdc0eb68e1fe0862c8087869b4a33e5c59929d0c8ae966442774e8236e588905ca40755e6c25ed0a25233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50ba90a94e16b90d107fcf747ecc0b8421db3ec715057b1610f6973d78fd90cb08731e5a103c32ff060730ba78f2734f0403b761470a067a2a7cde766ef926ea1fac0cf273a56e9b6972004d43e5afc55e8ddd192eb688a200e6d15c74f19b542a803b282a69e24571dc5a375d1252666a59fc8836b8a9297e5f6f4566ca3f32664d20891c5899217ba57148353e51fa685713236ffa70b921b92bf7375ce0796682e3db74c770b9236197dd1e46da6536f38f6773ddfb581168ed78203686d05b3ea73444452864750096120f1991dd629d29196e21f7c66f4bb5241c0cd38352e18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50042ce80397dea37792fce75a83fd0429dbe5fc2066bd7040327f9e2a7022dc37f730ba30af0efd122f9bb34c1d77456ad874d46e0ece7718a4452e6abc1b344ca5804a5fc034aa3f3d6d0f2d9da2024f2048b470eb798f7a0561eb69d1418708284262119c46e8487406875206d81c64360c12316e613413f10ca722a923265bbaac1016c98a0f1993937c23fef12b35902fec1904d3eb7341918543ec019944ba1c9c1dc625ca04ec90114ba9420279d5e0c15021e50971042c5f177e3141121e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5068f55e0a05dc755dea8c8f005136615388ebdd168c8d0f2fa1267a4a0e64c97a3e1969355172260a876ccb30f7f7a14123b0234e1edca051d51590775dd60a1986db5a36cc4c0349d0204e6c279935132727762530bc5b32c19c673b05dafe76837b2868647067152ee7985c82cbc4775e9471773179e6282e5cf95faa2616073ffaeb644f47e06a3ffe904173036b2d3bc72c55b7e4162078469a6e8b988a77aeb3d779bb91e31b95cc6e71e0768b2013d76170db5ea956ee809f3771506a0df2a8804e8104ae35f3ab431b56caca17895de927c7cd9c1fdf2a8d290c99814a9699331ccef5675078c4b96376d3e0244ca5725e7dcaa56fa72072242e4fbc25e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501e49b90985e72b237bd4ef1f0ab384145753e9192be24611a668b43d59a15361349edb6926c1344620035953262cd06ddc0d9857b98397240dcb81193cf73337f214600f71cf73161824240bc0fe355a2921fb102ffb890c7dadf06e4fd23b199fc6af63c837d84e94e46e1a6cd59154885fca0894184f497a497c4402c042448f75e845289fcb09da9c3e025da188166b7a1854d2513b03a251af75f01a235b6e4a726940df9b0b94d4102045585741ab011210bb9f5764288b7d350798da0cc2902265c096ff509c51b9203500fd2bf3d7313a54d33760c438bf1e30963554db80e7083314c214b8a0e31a9c470a2375a4071fb7e73e6bbb8d684335bcc158a413b26706205e19db4a665b4c6d14315baacf452498783966e7167c982177462aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a69ab2d01087989599b914573824aad21170f4e167c0da639361b5e41513d8c7b43824a1325c45f304b52025ef1ce9549b9661f2d34f64a5546e31f1afe84e17c9ad3f7571dea711aa68d495cbdf5ea25b9219c215cc8b3207c078306bc21c1725a8f7322e34113273fd8e92e8864553c2547390a3d1c3a5c70b2a62907874b5bf21c3b4f22e4315a7ac90134dd60e06d77d4b5143b2f1f4c5a481f1c8a7c74789c4cfe10008b0e67f0a755746e2d5c311cdf49050eefcf380ae9306e73b78b624d64a53281ad1500447e993d2331266985a1e0460963142523fb572a562a3c63ef53db265aafed23b7e40a614abb1b072672b5778dbe5e6eb551a46aa33b0b021c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5072292660252cfd5a86c852152e45c00804893d39f1ccfb0ae3e8ce0a8882866b95d41476199d35216af75861994d1323ab40a615aeea4c1dfbdf27077906ab1a51336932eceb0e2d93b552457d429e165e32db023a0acd1a77200d0d23b4520ab5654b2c7375b400eb73592c6baaf9494b2b6379622378546761bd17792a843aff04e74d423ace4a4ef7497025d8656a16368f4167a73d4665662a52d3c0c642d7a4766af187af79f2874d3871548c2afb5fe279d57b9e77c8fcb93307e9945aed172e49672c7029ebcce907f48ec50ada6f4205d163b71fb2c0282475316a43b7007d5767222808c954ac70815e41758bb6f570a0b80452deb77336f94a4d342e56ac0b6fc9aa182a73036ab0beaf47d0d97f4a123cdc3a8738b86dd2e047427431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a608b2904c99f1a3d4a51136f2744cb17a8de94710bd87244e09c740da4521d08a86eed56d9415a7df53e86382f31985d9a4ad85386f7d52e638328680894ee2fff978b51f6c62121ecb89a6254abcb407068a358716c443f55774c3780256434cb0abc7664353a314be27665571f39024176a66b87dc174a979a6513336acd1eaad0d2383a09e545d8e33a4ef090c9544c07fc2447dcde5c0ff7397bd105297a07688d64fba3cd3b16f12b5219da48007f189735bc73ce15616d98136b35ab2de160c55aed59981b09086b188ffcfa596eadeb273b9e790258e639790c21541dc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a5796a572b6f8535a337422740359e7b855273642c543f00fc363a38b57c0f677f7dfa2cf5efc56c399ef1494a80f25556cc916184c9ec2846606167c6f315421a7e0a6ca8238d524790d658469b0b65fcb00931b2c3bc25cdb80b1edf11d55f3903e02f1b10b527a879b84104ba1e30f7279d766d68504736394563fe41904abc0c137c6120d424ae0a457241715f25dc4515186d17692b06d2d71992fbfe40e1dcb8334bb81142471dd5449b56522bec028b6f12e8710765de070a658cbf3077e6c1097f1f1f55d2374b7baae8cc3ec7ac285f2f85c336a8a67d7d43b9ae0051027c219e7f7f50a9ded4586953bf4fe319507d0a6ed44f28379c6eed86dc6742fcef6c8818730794e6562698c224538b60ca287b5480608aaf15664443247a70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50816cfe2d2749a172edcb211f578acd6184d1562047277c0ac91a5d5434d0d92108cd407087cc4d47ff666f625672f76f56c43548ad7fd676316deb78118a0f53f9ee806875ae6532ac210e4fa1e9e32de7bce3492a8916637fc1e00f36608a14db8b8c5308759857ac38510d78d5f97bc1ff6f4340347e075f1dc02cd32123409c0d974c576ddd65bab76d505dd1933377d27f0d7e5cf75222eb5b68739a6303efd5dd773fabe377ef813c189537f72ee8f6df5079bc32553ce766372a1e063b6213e42bca182f3ed167b71affa4511659eff469c1cc0f62191aff6bf75bf743340a843391cc67266afa64201f4eb90d6ec1c407a8a60d4876944871bcf72137e1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a29cc1f77d4f3e173bb93c27341b8d90ec3165d41a1ba316db133bb16c30db8254913aa081c709a00b22625568c52d526faa0de2524149e0f054b2848dc254d50d9ad26518c13d44b8ae8b32aa8c476304ccaf823bf39f25404149d2331671f6edcd4db455365c10a1ef3e878d0a2543ea190ca6b8fbb7b40b0df8f2621edc72fdc60ba2309c3b73c0517d740ab680370d160d3125748d6383a0b8019830b2f26e0b6105b09db0450d8755b266cce2476c6147a0756079d6c3702a345e2518676e990453a67583e0dfb93db5bd1ee57437ba91a361a59c4150c1e1a2ea03c9f00e6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a6bb75704c6b3556c3f0fee7407533a02844bdb37d3a34b6e9fbbb65fab165b42bc8b245d5320395fefa6032b4183aa0c6011416a439fd0351458520867fb361905f00256a77a473d9ce9614b23b998553aed65247ece763af542b65a067e0d20ff12f2401c156150f908dd2ad6ab0e346c1bdb54b360fa7d1bfd173bcc76b74f25173e348d175f1a3c9a3704bcc69870c2d96c39ce9e986535dce752be1485430ef53b28648e9839c4845e1da3e59d40b41c9e443f29565ea5b5f54181f37075f3e17f326cdc793dd15a7b381da6fa5fc29a7d3e1455333ece942710fa4ea8714f82973f15a5c65e191c680bbd1a00237949457ccc1690461a84ac164fb4a42e82c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508fbf0a38ff0e15160ec8b33b2f1c11356b17ec5b71c6e04cbb31aa7e15d9bb7e515a25233b5f6e155aa83d13eb88065dd0fc3442584d9249c313460851f3aa1216bbaa150bf0f7585626251fa9382a74aa74d102d214ad49e75cd03e3d7fdc04d4c76d3fc79ad07d3dc3165d8cbd2178b1e8a15c00d7672f33d41e7d54000b47e343be4344faae63e1b26f605a26674a100b3410f5a2f62114f1a017312dc900c2de89374fd6fc66158be23935db803985fd5b6d70d37e1f6cd4af7e0a05627c8ca1f762aca8655bf5227b7bb17ccd6f55004e77cf1d066314db6824c21c941b1cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50eda98b3f6a6a63477e7dd83b3b23ae2904945928f042a52dff2d822555822d4d1a64a07611470c26d630d33d4117ab2e75b08f7bbce9386e7cd2b04c23e7c858a2fb042825f0215af860a951ebe5970dd56d080705f4eb19a04b031b7d08e3481b401a5c23d75434ef61dd7d265d8731862c21519c5af077f8711f0ffbe1536014a71f2f9df5a11407c75b10d55fb4132aaab93574637347cc3d7c5569772a71b0bbe74e7354da64f903e815ce05e26f12648426d863eb30fc75d84de0cf8906112d3e4e9d022912876d6345253a1679c1f2515151952e470ad0bb4883d00d4e3bd5eb407486ad474dc257596bba07608da43f709fa11902e86d025544cea82c081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50fcc190505b83795e01f7d93658e431481705d82844e26e1b1a294a588e47584730ca7d679386ce6b7303742c6a875866d71d007b9b60c3348602b87589365e0fc63a1c3ac71b0f26e649c705fa8c0d5acd5d3e19b3da446054e9e203703db54d209ca30bcd287514605f8e09da532201b4f2944a98704104516d9666b3863a37fc6a4e0b0a92b867d4a44d25f1199f6a8003813ff0ff4c0199cd2226f74e2801b120c166d0c51646f2b6ca55b625a31b32ffec764e3832514d471e2762b4c52af2a7655350aaf55dd9fb542ea8672060cb700e6bedc9c64e397d1d18b989eb7ce18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505532f7033d7c22456beb005be596682be0a4fe19ee463d198dba307742b0b22d2865701455dbac79a2eeec73a870242d3769621d5c82b627b4997d3e3aa01049af9be82a33cd174f172cb41e0e1b2651b06b1172e529ea267bb1f2205d345729bbd4b16fa34f7011e8138d24f7498450451ae64a9355a16f82f7cc05e2de3e479b298f5d5c16c86cc16c5d7a153cb006638a98641f7e3c2c8d0a085b7d22e94972b2d042c006e562933150445e1da948a612a90d4ccbbb6df653583ca840215019347d5f95f3cb0dfef3a31f77e980192d658238ab969d17a5e1f1641fd3de1733216865d0a5c37e4796bd317802da2d10f48f59a8cb010ea2b2781467237f0548111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e12cde6f9c14c9468028b36b1de50c1767cbe0189890f76b7a7d823f7e331f664d0d7e6bb299d56ee189b808de66a4477b58a8202787274dc6e9252989c90f744e4f6a4e6a1ddb5154720e42c9f76e018fe69a09bd82ce264ba1ba35552d6f3d3da6863af65f0860420ba42fcc375c0cadb4de697fcb30580c865e43c0c3b626ca59d136d18ca11c4b484816efba9d426a66b26adf76d47a33c07d4b260af20d721ed6632e586a5ce14483104fcd3e2ea7ef56463cf520761786aa063a466448ea6220126ff6057d68a71a1af9a8890bac53f23ab7a33b442f4f2b540e89bc0e28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5078480734e847c10516dcd4011f0c017e5fd3514c6838880248b2d807e7a8ea5f1cec724866eac57db968267e6585de27533ac73c8a642934c029a464e7fe2c3b32d488264b9b824a51ad58454f53d27edac0af43244e866f83959406f21f242c6aa45b404934690dce6afc149e996b54a02ac80264600d53f4a6813e2dba257ebd01a64b6e5a815b2dbd9321d4146669e0a4301cf9ecfe1f1d3b8a023bcf225d1b0f2c4f0f8cb4785f21b06ac3420e51d9853a74b3e92468c47e4144963e511cfcfa32304059786e05492e204be9a607206d7b51986d653b78a4005d45e0c37be6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a2677ab3acb74a53c40b8cf49d049b53a0d21816fee0c8450d1e4dc52e3a05f42fe66a87d408db60f560ba469cb51b020b56b9256c867f158f0764d661cf35f077918ff724ad1c0359e59d51f29e8496ef56b6d3a5b34ed60702e84083ee3c367c038b615b2b333757d81d305226a7b153057e81474726d63f5c6d4392be4926ed98a960f142b6f423a12d472fbb5b6706e786e45a57286437f1bfa1a5629d104b84af545c3d9b837cd06a75d3526453cc0d2a26ca76ac63576869a7117b5492c93cacb084830fb23eee96748e1b12a3624fb7d1da0c8b309d7bd78128240026ebbdff93209b8df6092726237a70e645aac7c270b88da415f3db7b140b02cbb5ffa1d332f00290d42c677913bc0ffa22513c90040ce317638b9a21535e4dc82112aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a32a1496198a1ed5962bcca4844768a1ebb2d083bf35b48377d793b39733b4334311c30143172c649b4c46a248458a00736a6a559a32d03354ffc533591306f571b70250882e56a263fc03a7e5332c26363b3f2290f750e20588aed72e110934e6da4762ec9cff76e051318490e5d261cda09a76428fc587a79ae75319774c46d027a483b66d93511dbcd420680dac24f0b42d765c00ddc68a9edd639ddc02f1ce31c27196a81e31b4ace1e42452a0b72923e8a24e29c2a3f26b5a033c50ee76ab4979f35ebf598189968de7c3509fc4d1a63e459a9f2cd6fde5fa9349a5e65608c29466582d8cb74da299265df222e3192d69a42fa3548722e89c256fdfc757cfa0051665cbfbe0b72e861535beb4e352c5b300b1d5cfb6756900a396da621419415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ad6559b23674935358c935738188af9713cf21257b0a2e00501c787309409bc3c87876636fda4eb29a97188077e5785075433055bcc87455026728069476ade06c221f234e4f44e2b89b7b449ff3499400ef3f627c54b5700587ce43dfd12774804e69e15f50932606b9dd525ee280a407c30ed6f6349ff46d6bcca588534d75627cd7b10df8c6c493647427734e6b603fb4db3417696da619209c13e55382701b9c7686de4e72118278c2f2ffb36d7762f0403018366f964d5954a0b70278c3b301ebb103502632b13d6a3580d77133a0cd4481f2cc6d94dc929db44f85a637c2576560d1a480966a27b95701f26c85bc2ef7c0fe50a537dd6776f06eb4b9f421a5a640e49da0065987cea66dcefbb281e76893ef4516b0498a15e4f0a603f55f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a3123d57d20fc1f3b1879f30731ce0f3b628f29751bbec31273be4475ffd2bb4ba9ddf1057929056b6cb03420cbd4791575c9122367b317349063f20261307a0d49ab721d8f0c8e6d05813f634366de2b2457f656dbf42a3e5b21d83b3395973151a8120e8f0b4628edb83f398bbba11f0751ed7ee03351622ff1522ddd348135cc511753cca76f4e56a04c495c1d421f232db651efeac65c8135d2636e5c7f2536297654a3ddad0d1cafc01c66f5a0492800206133ad51541689b96e1f681f6e837c6263e69b4b00f09f41282e1bab0fc4a8cb0fb8e7e264097f2458c528a21953f98c140d786117906f216077eafa482c44d04be32bb04ada38390205f46377115c643658d9df1267200a3fd051ab24eb92987837ea0a3dab6353707f9a094b70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e8c29919c36dcd3a014d8f6c2700de1469b7075571880e58d8ed5f3aebd0cc4e4a8769231bdd9e3238566e4d7dfff50da60b654df275070e87882902636522511f7db7117c0143798fe4854b4d01635003c356692b847d343ce38537d57b7e706989677d833b1662e07aff36a5d30659a8304327894b3232bb639041b4d64428cdf94c478fb3cb7b2415897068616b20ab7e4a73fe395a7d34eaf9029c4a2641e05cad053687116ddb307c4cb78ce823f1ede050f09d99456341f441ae4e6d6d7c9efc4c321cd1476e146f408e4d5d149d267e35f73e4c4ceec72f6ddb673935c6dfef3f9effa7047d4086558ca33f5e184dc0129198a44460575403a06c734004f8d362cd0fc412a749c15813b37e3a08f91c51302fe449ebcc136214369c2140d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b7d3135228c0812dedcfdc53dd6e656385ad803c9ad3ee16fd7c307113a25803884d5f423acffd547c2d5f11a11e121b48efdd31783a76428ad4637ab235a53a2832ea4163d7c4219f57d63edb99f3404d5e68170fa36771271899019083ff0b94d0596b914cc708a1f7f75f540fb6050c1e2c54d35eb678a3b5cc51e6ddce60e2327807e59b4c14ae76445157c53a259d76204486704c427c27cb17e2479b68d0b71c6ed9113b68b5f2404e291cfb50e877117199ce035725a1e04a0383e455002e9474a126052db9c12919a684f0284d6bd76bbdacbd575bc9401e6ff0096d7b45f343f5dd302ac49976780a87d321b2c1af6d9e3a175a81848a48afa2602afc9f355f5dbab711efdb7e74074ab91d5c45674e90939f762b2df2706a5ba52e19320c5c0d3c0075150f1631df546e3f34589130592724425836a16c4947037757c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b61ae9551ec80918a5a47b18c5ac204f1503615eb0251e302075657795e8f058171217134999a92fa357d32d75a48a78cabc0a19aee008450f09a17cb84ae47d781b6135a9fb032fddd3dc18822c616f3d60be104423c05c8a92be6f8a814429f58f1c05e3583023e428863f0bf9b03789e98a0a9b851f51f14dcd3ba1e1480bbb0c0d18f745a805cf72c85d06dabc3ec17b887660c88c451fc4fe64b594916fbfab330223382929588fdb075282876f906b562e92cf0f41c7449f4f786c790493befc2fc4a44736f4e91e301aef365977126c57b4084e506ef552274a91b03880c9a27c1a733655f2438a7d3b0fa138326c562f528980304ca13f45eb112b4fd2052b4440c87869f276874faae1037ab44d086b58451f6342334976500e9e70152d850c92ac5d2d4526305abc8764060d9f5049b98a192bbc5b2d76a294d03fab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af6592e0d512dfd71c5a0461b73ee794f440e137de6e1090c488b913f44b70d4652aa1357c4b4424d0952d64845ce014088a0b30624c3d5617fa80e552db4b76e3aa3580f79a363150988310b992d584972c8745c94319e63dd856b4a8d0ad71739b386595e76ae283d04e15b2472586b76f3556f3dd2e72a8305f67b000e076b87c340607b30da1cce4696686cff98365902ec310b1f38010c3611652fee63591a35452dfdf20a707049800f0090f002d7243a279852a624e4f59075e91ed8352a4db75b08c4c4629b46bd78f10771123ff5977316d40d5a7d4fc46e1930851d9c5c9c2a6f308d0a553cab21713a6276aee8466042c543267461294e2978f720429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa25e5f33c6851074a210ab60df796d53d71d56521917ae289f265f138f1e9d0d78ec931937cd0673250ec4583263b62ad716665809689102d384af7169deb87058ce971f0e1a7b7dd9bba63e1864ba37f69c4c48ca5e973fbe99d7216594d43ec5febb7793f9db48ae9a090a0e4f832c0e985c19d0a87652729b940081534a3687e38313ae4ad4226cb4e355903d6a4a98d52e69fa2968167041b60dc2057275ca4b0f256264f56b425f5f3bade3f74ae8cb2516067f9f29306bda721c909d054675fc129911a916638a0c23f1acdd1ab551427d93299e69342162689b05316368d36315e1bb8e5b115fc22c85dfb37380663851c388a35a612d55534c57c3692bd60471f63b685741781c372722033dee37ba533f1d1f0b13551404e1924a3361d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a682f5b41faf87c2b54d48027b9bfd027930c1f5daeb77b07a91b9f4dced8fe305213561c2ce6e276340a0e5cb2337b38d75d83750e2b71070e3445758f7ef3201ad3215e9a2c9a3ae5a27a4a92795f0dd50d6a33aa1869380e9074217e7fef1101e089060e299928fe7df925c9fea06dcea8fe2b8de46e50f055ab046861210998d12b3b017917021f09ab3f32fd6f49d3cde77c1e746a24a0c1ce2c1f5ba054b8a04817c752444144ae4b22ad2dc6458490ec707d89b953b0482f4f81c6661c90391a0d366ad0316944f87ad527d019cbee04524166ae69f8e31639f78a10785caed3765775ae00763c3066acef456150afc03d83d97962bcfeff3710452a6ce1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ac7bd0d1ecbd81576036eda25b85b33714c86d27b3577e512c76ecc5e207571724a1fd600b038fc0d489191006db2602640f31710cd27fc303d602c431cf3923a692b8c44ac395d3467e4bd4addd3395927661501ed362e51f3c48e42b197734846d5ed1cfb5f6265a1d8a14d23f42508f098656828dbbc33c7dcb44bcefd0d1f01af406783bda13ec3e11c75a171f4396978da122ba7dc496bc0b00e244c935673bd5d2ab47a9203481c143cd1cb8b720133f96e53bdb35f8a55e660039c3d087f91ca75395a21587962143d881f56470102715b32529035eaaf23263c16e6148650985d48faa71f1068265944c1d879fcc13017087f5936b3c9ad0ed209a728761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa21be94a02c3a7044ef54f2db2873637e7dcfb0cef5581257d76c23414332251f307a04d799f575b39cc7c5a8d0d1739c1fe364ac85f8a22b15e0d511cca985a9cdedb64a3a1a01b3cda4f68163c5c12d9bb054b7fd0c053eecfbe264829d8064c2b9700a46b273c56e4ec23130fbc749184ac2a5d38e31088ea5038b8d5cc0987a31b070c9d6c2ba44ca4683656084e23ca5a0c34b359646c2f861c9c596f4104c8a559f02dfe4461a1b81f9681e73e8d7adb0d51479838877eae751251ac7b7ca2a930c0ac6f081f843d5cb49dd20f4634a63e0e423c37d24f68501c8616483acfea6f932d3b1fe2f8770528ba005894dff014187a3b4bb051786beaefe81429d33c6b3c6103444d1d5466490abe196908f3334b0ac01be888a73d01026b14d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50d5b392005a992c1457d1c3104e146270e41652653eb9e7537b774f3d9928b633dac569627d1aa10635427942add92b2b92d7f91948dcb1548ae193315589dd6f42585b5945a37d664b991c07a117760f558de226e4b1f5092768e53a3659653cf99d0b2b236358642f9d956fe66d2d58f5324238b35666660f987f5ba09e95669215734e2c5764278af398210d537e4bfd83903f92dfa80a4573b923f0395469a5958c5f62af56053e879c721410c11568d9095acd574d071dedbf5ff8baf737e990453a67583e0dfb93db5bd1ee57437ba91a361a59c4150c1e1a2ea03c9f00e6c3224fd683330c0c3bae5076ba6e46ad141236a40e951e5bb144280e5233773110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ab3242670ddbbc67b64a32a40a43bb23aac58710a79df5b0f14085b5ff74f2479b408ae5a97b8d104414f9a577d5f8e2c77a98600e8c76c346f1e405d0d52837305ad2c0fcc38b47285688a45959b6300f5ca842822c74239c4114b783261b80e23c9a57ed0c3567e10a33d3df47f5b6cb2ae1f0d2057cc1dd4184971a3bfa864539f8151f0451b293c55d2083b26ec208da4ee7599f14873f604ef70628ea207463b430ce2447a572f18f304b03bb448e2562f715cc138179f689c029fe4c139e11cac36f9f13807d013307a879853775f651f2e586fbf18edd8c802ccb36c473c772e5fef3d3764846a6a4c478a3878ca6bfb1bc8da037154943542a8c1556563ed0361bcdfa906ea7a3b4ddaf17728cccd9c29f1226e4cfee6d56962ddc62321a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50788aee75572297634b2af4107275e400ee9ab240ec70300314f96d460883c86273a7d62635db4f553b7170414ea6f941ba1f7b7881b7756c2bea80588de0762e11c1f1220de60067a03fda51233f830e30ea186a6a593a0c85b6c52da326ac3fc57a5b23d85fdd068e49406dc604230205d6d762b6076e4788185c00974e1a4589d26835bb2adf181145b63c4854804156d833008a73f43c5088916f747abc5dafb18e1111ecb16c247da37a667c7f308f67df7587cf545eacb662779cabd5750ffb003e1cbd061fd7e80c5ae3b2ba4bde33f53e3bd1391875ad42468e65b4686d1c7b704ad8e8049da5f95ddcd4f342eb8d6062ea6d2155515b8f7b99d4844f081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503041676c589559021f901c10a217ef64e3c7ba49c41900516a6a666ca00e3e0e5a07ee77ee78051e44da0e47eb5879184345916296cdc6153dff630cfb40752ec39e914b6698a44511106d13d1c6ec2f116b4b5b8011a045cdebfd5645b37c3cb76a5817e748290cc4c793408351302a0b6cbe5e59f4274aad0d3f4d0a203c337efcd63d43ea0a3c5e8e8b1abc4bbe3ec411ca640e1a52635ab65254c5fee2003f991d2806b1fe7027fba6057d22da788c2ad166e4a92c385ec7ad531cf393302dee7d6d97f60c3b38af7d3bf7720944f437fb4195cf2d5df63ec35fa50dd725a8ee8b0e2050494b0e5f23338a61f35d775ad91100b99c6a9eea85311dbbfc5c762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acaa070269cd14c2d49293629a526d9604c2e13673b700d5306140365fa68b52abea27855d0224f577222b73efd6c337423bd0b108026fc6c1a77aa75907b5941002fca074965762e6fe87a70c5e29b75a4c54925c25dd83a46892f297ef3c50680349b72d658b8074c39da6f69c0c03f4525354de934951be584da4ba343cd11d43962697cbb1018c2f67731960be716878d9c1c3ce0d258e332c2467d77e448331237136934110568172b40599c95564b18032bf9a875694f93384b97e4d41f1536ce168780714e591d52028b05693b5efcf041ecaf3f28aa1fda5276b9f46ee2298359be7df819c8f9716f98fcee097bd56031aa261511b8b45d58b3b57420762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3af728b54f3e09285c01fe5c04de950b7bbf7f70001c27db5ac6b1ac5b00397116dd56875e4b468970631ca00a85f2ff183014402f55a87c05355553781089b44aa7bc906fb09035023b8c1049ef77ea11c505582c215e6241d15aa5747904b94fc7606c1c2f502a38b92f3e417df0ea3dd8a9b06d6ecae358569323106ef41d4519881a00ca5dae41f88bbd08de1ac87ab24cf8032e06d67c938a0b0cabcb24671552e337c9ef31265783cb67beed02730b67b95fdcfb2c6e8ca6b610ecb0106e1d71656082db334a0f140a1f44deb1744874582c2bf28d0633b6a44de83b4c20c52b90099474313f7e66b674c32dcf601b36a623466a436edb8f2a206d8bf151a2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507d9de053876d5b2928245d60542b38781c9e2c12f2181f7cde344d5d62c9924c67ceea36b790de753724ea27342f6142a949a02d67586e69ddad9374a9b3036f2c40b52afb467f4bc487e049553fd7567c7526429512b6274e32a65228c3d141018b457a2b77801d399e353a1a59861cb108063e8f70791a5fd9ef6b3bc0851d96a52a6a5043f7520021d87b16ab29756d518b780781ef1c2d2ebe2d65f7c7057df3696255ff31543f70b759f3a0c248cd40266fcb9d227d667dda48a7323f02af34ba64dfe9815a3fa142465c4a5062810d31402d1096347407ad15904047153090493449c2e66d65ca3155e4479b29d2bd046203c4af000c90961348c3e06b6b8576259c84fc3d8247a46fdae6673791068717a945f84d19422841358fb63cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503d5f4f483be55546c7c14c2ac7d87c38afd25a6aba50d60b4cf2413dc527fd3b1d35ff2a7b629e790115960e52447f137a7852383b25a00c2674dd2617748a5b713d703368a268721e7de82deb194c7cb9efea3b68b1d715b890c32b5b41240086b3422a27ff7b7bf47a1a1edc02534092118f0d5e6b9c17c6d1ac40119e986025276063e641dd118a5abb6932db3b10a0ccd664db6908020d6d1778a644d011dee31329e7c3d4660888e300295011567ce1722b93e1fb6fb4c3e46b5cf31936ff4bb45f4f5e4b1d91c21866cd425d34a84ef05c660cae37c488511d9e7bda4d5e78390cf6120d77cf06361387e4eb6c46fa042f57c0db1608abbb59a9218103bd193d56181be37d5f06b87582168d7dbbba590970f9dc0097eb8c19889f3d12f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aaa2c445871f62100a553611743fc35798e3a233e989b451eb928c52ff060e0162d9fc70feb053553ea6a111f2bd8246bafc80801cb1ae8132dc3ba2c4f819d0f1845845a95164e133f2d3b54475d8d687951e821f108761925666c75212aca54d2731a350aab520c5b4d28179f86916080d9971d757d391944183738e1b0ba7d5200766952715a7621d1c36a0a83672477203e148ef0b5658664ce64c80a8012fd85144f0fb876713c298608d2177602910f932e1bb8a8668cd66e12186ff62c63e630794989b965d7ede64004df19049fe6ef6af63f213f028fa058160aa53576a6333f4297002b7bc9cf679da4221f0b52bc1c670a60411995e66254a4e02b429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a52fdbf2bb778ab3eac9fd4569cca2a6504c147757fd40409ebcaf03400f53b360478a05eb4abb1306d32d92f5ae97f6eacd4cc76859f396cceba2a775928457adb6a915b659dd80d0a6fde1001d22116f80dd6769b1a5909b308b8196181a42b995159048acaef62b08b154afe1e201ed778d645e680972862652761f9ecf41e5e6d17085909ac63bbdc275d0bb56f00a7ef794ed9fde54d77988a0120f25055b23fa52c7cccbd1271731248d127a16838b3872b4c54660c8b852d456507b7579d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a506e54757dcefac37303be453f3684191b5ae6a64e7e27af28871173692970ac2d5324b95d42f1616ed9b39e24873aee78eae41d39360af13041319b09023f0d2a2b3c4158570014272b373577d1137b577e8e536ed4e53a5278c7392b48b975783be07c35d03513797fbcdc180a30461e7ed4453abcbe8f3533e0d90401b25b6c72fd8726f4ab365faeaf456743561530649dbe5ed4b9ff542c919b3b7ba67949b5f487670202fe5d0e31c07a5dd8586fe4fcda5c01b48d3ee7f6014b1a1c246a1e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505300cf23483b2c0dcbbcca21e91cd73beb396e1639150d18adb5c375f26f266bf66a2c269eb17434454a9310df14ea5a95047f15c64e7c6e27cd701a328b0c305fe2d226b264b77a207acd12cbd7ad4b291ebe609db54e1cae50ed5dc1eccf6fefcd85238584820424d00e5750bf1b330fcf797dbce9ad138c8b5947dcdd7e6647c8541b47ba164e716efb2dff4cf13cf7f5f565efc614728b633f1d64a993755f5c3d71a8cd506e89940c02c20a5c3b1bc8b63c44efe939b346950c44e9590b4558f778373f140b65f4af150e641f051a6c393c2a21b27d05e1ac029f152a6b11e9c87ee010312eb5274e42d214e50dafb45771286333604878897846fb57555a22a249c29ba54223c6654b3937ff714b4bec0b97e8565cf3982a259807fd4430efe742f8a3b60988baf1426b56dd6151167a04d9c5e65b99e085776893765f16aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a36c1b43cfe64e732b7ba0431e40c406d373df72ebda8144db2e2ea2b87f67561c9a4760c1e0e3347a33bd22e018fdf26aaaa6508f568ee4341a4de2716e5ad4f9b887d5c3e6f79026a1df30c13018c1dc3be11259280c02f92bd8b1579a4950e327ccd7ea2d0643e6ad0695cd5c203459ef20477e723e72c361f4665517d623556053460b2f1717164b2832eb7b9ee59d282c473fb35f455648640696f68d92f73737866388de76adce73315be4b81799bcd13196f0f34054eeee016f7fdef2c5b7e876f5efe87156c63bb55437307079f22b221486b4b629b3a6e517c58db39fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50432cff3c6d98d9593a6c693159b3de3611c923004a5cc91984d2156b40c6bb72a38e5229cfd8ec574a9ecd4eb7413a746b19d5141b9e6744b677403a26b5852808bf0839fcc25b708b8bb62a0623476c0f7eaf6971548111841fc35945b6710c477be861a06b6874a3b0d85ef3489313cc161d31762b7310194a202a5cca972a1d140754bac9124e0610c97c51365626f8d0960792c2530aa80e8f089fec7e1d70b6b9318e73562529e06518973dc955a4e01442238f3c148c5bd16ac9ece2649193600cbd13f36068b48416a972de4d81850d4266ffee6f515eee731ddace0587b4ad0bda761c738ff06f54e52e320e7bb89c44f7e6784bc1593936c8fb997be26c077d10d728320cf9c7797155a87b1ae8362162f6477d1e89ba1605ef83426966e9481f9e6f50f60cef04cec6c218c563630750c1067deecf1110f37dcd5db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a445d5b01b1e3435fae713348bf8bbc70285a165336a8bf6155bcc533b9dde913fddde82a34d3fa1783ac474b6203a9706f3bdc2f99f96e1c60d55d7ce0de4c398406e350167f175437724a4e3956d7572af3d662f210722f177a2f71d56ec9017e941f2ab509b14b1cb1c44104a52f477386645ebfec8024d0c5d30835c2787d40a0c3212276836635d8e524b9ea3322cdc3562c55408c110566585e25de7462ccfb7e5eea1d77581e2a9161f1603f42f4886c757caff573c1f798272750c6497462df04109c2d4c5bb40b125e070e7cc3d4636c57d7f7003fd7012203b8511bc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503327b927785402420a87664066176155aa51376a56b0e2499936b6671930c3695865e85eb23bc36d14e2e7460e153a5fd225de051d524f74d1151858ba853c083cc1e919b537977224e8f509e6628568ad939805e49681620c20cf221fc55a12577d4c2ba487b0673c08276ab37ac75990e6f9435aa53576d09aeb1ea820c1185fb03b19372c31516cc14f4a0230207e60a63954b21c702ab4fc5867fae21963dc94d64ff0c0fc49ee0d2c7b390e985e122ff116cec1de6552e02a19a9f20b0492a6960f39247547c07dbd616c4a8c2bd725fc1405bf0324eb859d65fb6dda596e49d17b02ae50214416427a13ad7e23ea315200cb7f9406a555ae476beeb06e1c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507f0eac38403b600e87dbbd1fd327ed68e3b4742903164e4e5a0bd804debc404cb8c04812af725f1c12269f0c1fdb2d12e8b2be2a5ef4a019737b5a29934bf249887e4a6165b17a0723c36c15ad91037b7d1d3108329eeb0fa5918c531d3ca264eb6b1c6e28b24a6e6085c17bc25fd10db8945049fe6b1440da30a868ced6cc2a2046602ed18c973522445e4be102dc5e7ad9f342b991e53ebf5d6a665b9d5218511405412aa54e4fe54dac0707698c49878553099bb30408efcc5971a7e5b60b77ad90087d13bc730e6f42031589735fc45bc945878557001373d300908ae32ff4665043bc0b5e1bd5e6e13042ea4b6882eec62b90a8444b2b7974661227757d25d7b53df8f30074c2e4707612449b6c6e63d80cb68ebd3498526058d4b6a52f0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5088e886583be13b735921141ba3abdc43b4eb377e98ad9423bb98b400da63763d713f3f33af39067892af75793656845b78250402e2824373b5132a419477a6180976874a07f82863dd786b25889bd27a368bbd2fa0d4ae3a4905eb79c63048799807e06437aa6040a116d7355eea6e6aaa6d793306f6847b4b5b8b6d47224f53c2324442d66429125102f3763d07277258f70170caea084149aa3e2efd3f8c03667d0e5c78abd07d5307571622d25d548aac713011c24040c9f5ff5282a67078ba9f767c51689c454cef7e05d4cdeb1ec2b6b65297332f67e8dd385476d6ba2a7c8f7f44ce1abe32fc96eb5cffbc4006fd97303c15b01d507c21f63f18e82205cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e2712157052d35062e09af4fe4d47b14daadff0eacd220189c0f5d0753eb9462361c5242718cd109e68930289c78330310603f688e074e3097cdc726c8e05a4032fe4d3cfceb2c5d63e17f60fca37676b6236909fce3823826d1d40386ff8f21ebf5b67c49eff200c5795d7242a57429018b5d1bce21b2233c96781276eaf83723940b618637be771ecbd83b2cd07e20d6c8cd5f6720ef0fc25640193e93855873737866388de76adce73315be4b81799bcd13196f0f34054eeee016f7fdef2c5b7e876f5efe87156c63bb55437307079f22b221486b4b629b3a6e517c58db39fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50eff5f73043680d66a7c8f20e45d10b23cde43d0c53b74d4f5bf3ed775c09b60a66ec052bc34e586c699c57622c42c647a606b464bf06ad7174795d4292dcdc4c4dad840280199f051bbe8d78d98b432001344800d1436a0edc132f54004a6509897d1e2ada1471727398c11e83773b47c1451d4d5d80ff551fd8952e4df16370bf8eb5090b24941ffe83bf230036781d1ece4d60e6094955f65cb165eec50e629208297a6321b003fca5950f3405c1159f56bc23c2da4d51c36255260cdbc927aeff9d3bb5e2eb171fe2df26c5c4aa09bb6824707a8648479e5cba7689792a21987a0301e14def511b9fe01f965d5c36dafca71768fa74097e49e84248abf9133110f3225f220b6ca7728e5e46af234dc61ba0591138a428534c1209c9e61c2e9415931807c8283ba1bfa9392448db0d07a44f4681270e46f25aa8257ef07619c8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acae7271fe0f437651267481dad9f4645636e664b82bdfb3cd5bf385e29f6c82e092ceb6674af2a347c2bd119143b957eb483853a76bd7756f8188a45a1e88a602d585c5dd4f33313bf82f7330cce90462bdfce446748071b55c59c54efe107147a23420aacd7b22abb185135cc0e6d4a3abda0451eb33d4f5108b23eeb77806aaba1d277e40ef24471ccc5671508cc3acc4e4756ddfb6b27af7a563f38804166c447756f6098253a1ec7dc55e4e7c969a6cc2e3b399e751ca6f3c778bac7ef2e1f6bf42526054640950ae575e7adeb6f7e874216ad01c54247c4f8559f0009694ffb5f48ab93674c31a2b90d1f2eea431fae770370c2df7d2aaf841e373f7b1dec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3acb8c3c4260b19a066c453452a9caae7d82112617bfe9db28a7e7e96fe9f87c4eed730f2ef7aa623a952157425c0b335658c68370a2b9851e7139d74ec0c214390e44e334247c3e3f396b355614ef5253e7b64b6ff0d6080ed2dc403dbab67b5fd8562d3f6e4b863bcea67c755416c326bd55f03068112617eb643116a0461d5c7acf071a51b5242685ca8963e9584d2d500c4f0085b4772a0a49277d93751b4889ac836c71fe6d34f5dd293d98335c481a153e49be0c6e22ae5e34308e3f327845843a519b62f12273d0c30a4ef7da187341c010f21fe1008e20723a3a324650446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501895a92c569cf8127e5c1448e41854341c9a64299396c833518e8043be5ab310d1e34c30de9a1c3eda7ade05fbe39f52c91fe95bcfa4b60201f8c745cb5b34168e97591e3e6c3d251cb80973e8217d652abc51030be47a3c98cb066968c98a3e588ab854c708787b6c49a02565cd7e703f43f77631249c65fe337f21b00f572a5cf10978baccd3799522f15224ab667795664c348056fb3263d61e1d434464725af3023a02227803330436652154c7298783b33da94db25202e9470d773979098a765967aad2c2238b20f4400f897d4874a02d43d7330f5bb7b7400935f90646741c825f8069eb6e75b312384b42d13d78d81778a818e3330984983f3ac3d01c888b866efccada791f84996856da380b842376212cc0f65e6d936658e9b9bb78dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504896e440b6f508067d7760363640bb3443df881d21faff718ba55d5b4e7908304aa5c8664481825f8a201572d4513c1d9348b613b676365b352ba540a7beb3095688b641f3e51543a1ff730d28ae815e90ea296c07eb5245a4b1fb4b1a26bb7ed7c94e24508b8e119bcf5a3000affb7ac1c09f7870090321b31c34087a492c279d6f355830242d6980874d18bbbbe111df9c1d751aec3f4296cb837c8f1bcf0b618ac2275c165e11f60e3d510a921e35dcc0ff51560b7b1959e1076e7e21ad7e4ba22c7e65a95918b44fa021baa9453e0edff362cc335f5623a6d660abff77541cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504896e440b6f508067d7760363640bb3443df881d21faff718ba55d5b4e7908304aa5c8664481825f8a201572d4513c1d9348b613b676365b352ba540a7beb3095688b641f3e51543a1ff730d28ae815e90ea296c07eb5245a4b1fb4b1a26bb7ed7c94e24508b8e119bcf5a3000affb7ac1c09f7870090321b31c34087a492c279d6f355830242d6980874d18bbbbe111df9c1d751aec3f4296cb837c8f1bcf0b618ac2275c165e11f60e3d510a921e35dcc0ff51560b7b1959e1076e7e21ad7e4ba22c7e65a95918b44fa021baa9453e0edff362cc335f5623a6d660abff77541cda5c20f722376681e2d75b69bdcc5c67190d000a65fd7429c2970dd80cd74f5f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508adc2a14a4bedd46810add2604335946bd3074591709492c023fba2401373d0bdd0d02043c4448045175c36e7887395b019d0145e4f476069fe69b7b74f32057e141982c18bbfe40225dd56ae0846d7517b86d020402b5072355e761db72933d5842b729e58e412d99c2c30b66590f500b257e2712454f158e7b7c3e7e2d902ac8ea471a497bbe55078b4a647b5ab2355d89961c35d800016130e31cc149db7799c16b3287c18f0d09a66a6fee1d0409351cdd3582097a4b9eea6600bc34d90f6625281e0ac19324f822fa3a9b410c246953b55b636e741686d66a402b677a42f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ab751785c40296c1284592f33968c1d3f274e5d4795a177657b38c0455cc2e15345a36d46de535c41a7be14159ad8c90f1dbe3f610ef0050710e1014cd3501e23be3819104a742b233e3dd712cdc1e23b07ce982c3700fa2ff36ab7074a1d9a07a008373e1df3da6538dbce3356d75740545bf20c27e0e805d762434daf120261339c827996794b2c3ab40f7c900d471f976a36224487572604b1712ebec8e44987fb8b19f8db440ece4990090f5d7822dce4f01416cabf5949359a25aaaeb2596f89c531a1f3e734588c656ae3de354e76247a19dfb9dc4203a91a5855cb2748a30ddb5c08eaba75d946b1569579c3362aa7f12d35970816c6d76b6ab140047cb1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5cee9019e45278257d6bbe63e6137316a940810d408f141f90b5f76c9d59f64a90dd0c53b72996667cdc0a58d43c05396c9c53031bdfcd1692f7596753d9302d6128e50363e3c417994e6c7af07a7d0a4667a52a9e5075016d70ce51f61c1b653be07c35d03513797fbcdc180a30461e7ed4453abcbe8f3533e0d90401b25b6c72fd8726f4ab365faeaf456743561530649dbe5ed4b9ff542c919b3b7ba67949b5f487670202fe5d0e31c07a5dd8586fe4fcda5c01b48d3ee7f6014b1a1c246a1e9f1a3e64a59d405cf7546d8990e10bfaf0b147763a463b0dc929664600c755fffc32665ba626645843db78bb4a21555760040bb8e87c781a0bdc659f09d8019550c810be76db01ef552510d1cb2243a8d0e720100bd63d286671165b4dab349dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a507f482e0306ed011cf3c8652ce9c4447da9249f431f773c628f043b5db6cdc031837ef8215dc9d54bf144f95d2c84652a835ea1751659962a065c410747b710593819a56eddd3503b0eabc37be1a4a00cbef4551f9983ef060a60264ab0f7de7ca39a940fcb23e12c9f3a2a5ffd969926a539c72517b3f33a28866b77be6c0568e9d10250f48a625ebd12505ee154ee58d544b05c800ee7782281f61cf91ad6366de0451b3bafdf501769fd4e2ff346665b9ce832dde7bc5c0c5234336d2d6164f3e17f326cdc793dd15a7b381da6fa5fc29a7d3e1455333ece942710fa4ea8714f82973f15a5c65e191c680bbd1a00237949457ccc1690461a84ac164fb4a42e82c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50504762008977b434f19c5f131856f46fbeb93f680829f14a87c25961cb84eb3e741e3105f098fd2de97bde28d8f7c477212a241e30f8d53df77b3c254a1ed976d0fea73337b66744e42e9f130cb6a77cafdd09406d715c657e1f63592b4c8a1d3fc3051caf7cb8358a1f1c74a34ac02fd0dccc05eb280a4e3ac69763981c0e1e115a1e11baa56b06d0eea34285819d523de8375b0b90e233a73a527a630c4a56b120c166d0c51646f2b6ca55b625a31b32ffec764e3832514d471e2762b4c52af2a7655350aaf55dd9fb542ea8672060cb700e6bedc9c64e397d1d18b989eb7ce18b4a62d465ff7be8200a350ba24d149a84954998f8720811a37a4f7043ca0f2db52e36e7f0177a6617ce746519e0546d7b2c70b8970c081fef4662e4685d67aedce3641ca78c4a888d613e02e85f787db78a0328c4a4289de31f476870d301c6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5063dcd12c93f69c7ef9d5ac4094dd4f68926248359cd23d7cc8d651342d78bd0aa6f7f63d17ebc4200c75be4715f3ee09b4aac64ce1f6380d23500a195acb1811b3c4c330a6510424fa37ec267a53577df36dbf63b7e756422ab85f1e608bc2473378c12de6d1613511c7415a920b070e3aab3b581b9e906322a1031832c72f7a1dd6cc01af082817f763c1732c0fcf5fb9eb5b7e1c53a84b717bd95d8bfa1f35a22af2776955816fceb3296b33cbe05a4902bd1601c8b85fada64322089fce6c21846971431a11332a9a6605aace0d3e9a1bd244e1126c5da6a94551ee27d9082613d41f2797bd175834a8733c40de635258585c01923850c4f86d07cadafe32cc9034658f3b6f3f3d67e6692705a66307c8364c98b43364497bdc55fe85f02cc287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a0de90b195e55e32a7c55632a5c5bd31b1c9a7425bd05b5265dbdc16bf4e4b07dd041230f909389280b21aa6e100c88309e17b546c2f0a526631c2d378f4179472b93546fad436556a821b03170d6764a388d052e7013d803a579d302d76347424891c151ddfcdd69ba49a80546207d6c6fd78e3a8c284d61d7a72e444acb024f47f1b7567d2c9f24077d7e24bedd6b4250e6633e9be62d3dbfe2352cb6f97b0fbf1027180953dd791b5977712aceef40de90ad1e6860a5316a5ca11c1ecf1118e9fd874977316a4c0ccebe02aeb479682bb23030e523f516f2f18a40b416e543da00fe64fd0aee04013e583652f46c2fe4ca7261f343f95b6bd02143d46dcc07d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50bbedfc4d7bd98466642ddd2deff3f2514f1994508f45a5323e3e6417b365c306a442c91414006117d0bace4acd1dab5f5c4c29562bd2631e290a0572666863253c418164da8988393cfe5f3f6244815dd621110d5c514550df4d6d21de4d87236bbab447e7b77a0320957354c6515c308dcc1a3ead2ca12208d3ac6ed83f7e77fd50454ae00a1d151329de3ccc61f91009595d737f17b07c3cefd36c17b1534e71720a46f0dc84477d7bd33c59d2487af4c60767545e0a1931519d441c301212832aa842a50aca03932bb5005709a60395803c45dadf920f3ee758340803ff7afddffb26d99e136d2a2c592e07816112b69e8601a730323f68abfe435581686582c978098803cd72e0c4554f0029507a31b6631aa9af43782a55922980bb5a7e7991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e1f58c2414efb77b85e4fd69e7907b24d96516543c7c034b4a7133107084da28499a027db515ee496bb1ce00cae4716583f1d76cae942530facdc71cb17d9902b93399515146ce221e2bca62f3db7e187e51a801f492707b91edf34632995308c5867b566769e94467967d45b940c65cc6ee676017c01f489df4a22e1f3c712ae8e06d01967daa3d075d192655aace27971a5e3432e98f378fb30a622804cf31bc7c3054bd155857a6c94b560ab94875d3f71166d2438e75732f4b60d079dc1a1649526ae89dce0f3ecf8e323ea1673780904c04ec29004b69a9d47a5f010e6c4f67f045ce694b13d3f5a6264787314cf4f34d0b7a4b5c570e655d51fa47a76180edb530d3715b4a5e6b9e5dd071462d95ff7f3aa9b2351143ea5c7c452d0a0d7431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a2f1bc9229352da0543c47e661fa3e4486777931217f7cf330b7c895a137ddd2e84d5d41f0b343464afc62c28bbdc9b641231e578ca58303871efcf66c5ad2f365780de68fd5bad5d0a0dc640fb8e4e55d5c16e6f3f269718744d6c1d6540ff3e733553166f6d8d5996c8ed0069611a152cc4005bef13aa1a691cf802ae5d4e567e49fa05d2470b4e10d83d362a613e3a463fea36f5cdcc0b6cbe04189d1e3971e0663525f7ee3e5cd8469a56df3f187e0e52ee18f481ca7e3c19477017988e7863e630794989b965d7ede64004df19049fe6ef6af63f213f028fa058160aa53576a6333f4297002b7bc9cf679da4221f0b52bc1c670a60411995e66254a4e02b429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ac6a0612b6e26d47900941a48188c6645ae65cd561867f61c3c68d636bed5ab1d9a63644c482ea30b4b67d93c572bfd52a7b64632aa1e867e05ebe62513322f59698322609163da156441482e69cefb3e16a0c704a13d5c63fc928e5f53d51b60eea78a58d182f24fa8bc9b6f86eb180c7163023e6fb5dd31590ab6072da798488906ed3530b9f4744a83ad0dc1035b4c4721b47d896962550510ba156630b318d44dfd08ac0eed3d22898840427e5b0983725f20154b3e766c368843cb46205ee9fd874977316a4c0ccebe02aeb479682bb23030e523f516f2f18a40b416e543da00fe64fd0aee04013e583652f46c2fe4ca7261f343f95b6bd02143d46dcc07d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50166bf204921fa05df1c95752faaf5572c84f6376f931045c42a8dd1d35644659c9c1e424b2626513e9033a6347ccf064d10db925dfe1225dc46da3512b781d2c1fae59276d153357c7339843de3f8d0a214c0a11776e884a4ba00c3044ace336bf1fd606d092574391cefd604746fc46902e382dbe9a584eb335eb7ead8fc73748b6606656cf1d5c18458d3b7fa5f3441346f7233e975a113186b412a2b8e514fa68a62a7358b834afdf251adbfb1706457d3765f36a7934cdc0eb68e1fe0862c8087869b4a33e5c59929d0c8ae966442774e8236e588905ca40755e6c25ed0a25233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a506f1df1013abd7a466ef2b8510afefd458046c514e10005667e1c691da65d441caae8940e1bff4f2e3985db034de8381695f32b6e9c53be60ac008d638071b14be7999c3d1985025a81d0887dd79b3b57ba65e1007029f16d99d21615183b982756aeac3adaaf340144d66e4f48e87a1ad865e44d3c3829642b4d3b47754d8e3a8825da2efb3e0161edd7b672d3dcce1bd26a522004326725b9b4fc45a8144537549696377500da0e9e04d4295ff6095c6b692e3c66cbb81e475a854e9b5e1518aa309f10e701a37cda0b863b6e34da3bd0a73358d85ce512cb4c742941421a1ada2e296cfb8b9d60536e7a7e59503e3d26958a6f218bc40ab09fa20d84e1cc56ecbcd547e5707843eb90041edc2ff20f1e60cd42456eca3238d9572e942afa4af3fd880ac753fe5024b0646d24faff50cbb1dc49208ac46a7e8882179b083951fbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a04246e5c6cd9022813991d5b711a883623e5e7393db5cb7cfb0b294ce723f13303c70f48da7e4629e3754879ac98e93ebad3af21ee4db740232ac603b5213d339ec2586dec5d7a2795e69d370207cf3d5e54023bfa145e06062bcb383508ad640b81f7108e84fc39dbe82216a48fbf600248566e8f113d14add58963f2abab724c3e87682dd582239ecf2a68764fb456c4fd495402523e4141f5c32d8e2a7423e188480a80994a5c446206567c0778689161b80cccaef6547120125378449b142d57c86920d8dc6770d8864d0f206562a5d891700510d24f3c22ac6b7995766e5d43c505af8743798f07844cf846040e82d48c49fad9851ea298fb2149b6a441ecbcd547e5707843eb90041edc2ff20f1e60cd42456eca3238d9572e942afa4af3fd880ac753fe5024b0646d24faff50cbb1dc49208ac46a7e8882179b083951fbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a90f088505c733940bd25490f87a780717d12682bce986e08ff947251cbdf5831d6899d10bb1fb12266da2343ff178e5a6edb3a168a1b992d8a78671b5d140a799c60236e8e4d0206366c4f1ecde11e319133be4dcccbd8470f9c9416efc2f73c51584f44864da5511b8be27d8111cc5107e78932ec37ea1928243120513c567dfbeecf1b5374fc03905f195158bd52282731d83cd23ddc22a22dea43b1cd984497472862dbd9e919847e715a26dc441db1f09c2e67d8387beb01090ab361bb0c8c6996307e8fbc291d2cff08425eb6050f02d6240b06cc13f145963ac2cb43442905e270eb4234120141dc5fc32bca1884f0453951e1e2088b103c3023bbf1595f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e1d8533151c89015c9ed334cc4089342c0997e1d70552218598f3e7144545e7d4d63572627c405170569145606282425c688db22f89ac4538187395a5e4610162ed06d65dfded91e2f9a08790749bd07a0a5a90b56a3d8603e09d941ad5c2d580deac47d9e6d5102c26f8a57d22c2f017f8f4748a4743b05c4bc3b393496ab13582acc4f89b35601d681b5654360fd2713c8953f7f289c02b029910435d59457dd53e77072d0c552e46cfc055921d574d1c4092ac6a7f747f07fad6ee39f3b536e99202eacaaba1d0f8c4363fb1540312e0faf42781d513f70f29500ad14df0920f47231a1073d56b269a7475139c25a3f21e76c8f4926509fe6040260b7e773888b866efccada791f84996856da380b842376212cc0f65e6d936658e9b9bb78dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a500ce22f6ee28171139c0d5227b5958e763b48d20cee88a56f689830250a68846a597e8a304c1cf51f961cfe6d55e9566210ff0e43c4a64051e996f93035e8155b09fec27499e9c36efa2e8b5e85b313332026983eb58f633ba647453840a72f6b3d00b027527eb115607ed40f4d01da19b5f9ec424fffd927034a282d33f496575ec22a23f30ece6fbe83372581f470061c14383f714ba21749424a0ad186ea1e054e6856041e8914ef2e7f2efea8373dbb263f04675daa45e3d80265b7bd484ada1d1226e1113f7b9994ea42bf572355ecfa826248ce787be0f23c6a256e7e2f10aae743e243b7591bf738796bb5173aa651657161cdd747b0a4c15603531b285a22a249c29ba54223c6654b3937ff714b4bec0b97e8565cf3982a259807fd4430efe742f8a3b60988baf1426b56dd6151167a04d9c5e65b99e085776893765f16aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5b1cbb70ec556b20373c363f1bfd695e61ce66420771624794fea718f317a13990020b2f81610571393bac4e6e95d500fd984e6feb40d143e15f3b2dfc83c27da3f03d019d0b846b2f47db1e188e635afb71eb3c9040a9375f6fc66257b4056ea924f0759b5dcc0ae25a8e2699e11d2c347e170be1537018305c9a631ee676273898df2cc0a74c0038164e32ddc3a7418b960734e16bea3bd4a1465077b83f2fefab576c018eea636b00643576593667c6dbb52cb5de37336bf9110d05000a2591acc558aebe1357bac71c57b7d3c773ec5aa9524a09d2400642c414178d5554c52b90099474313f7e66b674c32dcf601b36a623466a436edb8f2a206d8bf151a2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503ccb4950c2ce2732a673023a05c1ba685dda7c6d2b109f75abec381d1f51e96bf0122829963a686d4b0aa960edaf9307aefcb019b732a63fafc085694dad281140bf0a532670c23b0478be57a25e6b52a48a3002b1d318693d2e662a7c249807d5b7565f495333693d070d79161d05176f19377c64c3de4c0951b2609ccb8d033f89412e47db390235688b778de39127f098dd74d1813241e0db4e1fef5dd03b104f1c4892d96c2cde1752051d0e871c8caf8b367b83ee746095b72367e9cc187e800e1fcc11351fc89d64760b11c768c4223030b08d5173ffd71b2fc8b1457dd4ec69727adc0659dc21814d5d027442abde8e3d1c71493a12a6ed013db2090e0d67463a8edc5557d155bf686bd0530f74dd682d7d9e5d0d3e4f1b2b21bc7b1f25530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50e0d49f02edce060ee5259068b23bbe0a145b6521830fe9546da3b007a2ab84010b41253007afa94b2ec79b614621744640b986441c00645d7256d13c99f3d7373d26056cec87db7361cfc40c18ab6d1149be204d5422614511e9f555c66bfb015f39803a55767934e9cd807abc51107b0e4b33023fc72d04b1c8bb532acaed178a37e54f52b40e49bed343106a598c2c6a90da54dd525621249de26c70834c64bf7871318be210151f4093637517ce749da8ff356be0e7333ea9e746487dff6490391a0d366ad0316944f87ad527d019cbee04524166ae69f8e31639f78a10785caed3765775ae00763c3066acef456150afc03d83d97962bcfeff3710452a6ce1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a09ae79459184ea2b8fb28a6c671a1700f832ba5944172630a86758520ae0a65442e5cf14a1ea8172999050327a4e08462ba8e109af840d4dbc2db319d5eecf72a3c64e63f9651d03cdb1e43c6855f11a87847300aa59ee0136fdc85bafb5230682cb833d26d83b465170046cb89cda60a40ba101e1c2a67ab1b28e08d1e6930bb0fa8649416c4318aedbb04295835866bfb63b6b03a5fe02e587c70919976e1a494af83427a3955ca706655e71955c5b2bf5211e9dab113c32ecc46e4ebc1f7d9ca75a674de5ca4726c6690201503159daa4a22dc57fee5b17a6a8725b7e7761a3ba4f6a1f571c5efe685435eeef0b01de770451c7ef701407196c001a885744a1574e5337b0f10f1be95f1be7b50b2f1c03cd3dee2ca300a4b95c659557b3009dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50f52ca4263763994db049d801485dbc2bc94ba35a3fea0705899c1f34357dc46d157ce544b6c9e063f470003adbb0a52e8bbe29203b323a5d2480b616b6212565f6d65e1a8e9e593b55004e6f636eef74174e397d40b106063007694070c7e1598cff3f5f2860850091c5c702393d0c0037bfb91e995cb916785e821de954d845aacd914a2a32765719d2633aa39b936a23291e3b2730bd73d759ad55b7a3ed6a4dee052c4bbf656551527257c197d30a5ca64131bb67587214f5ac30fc4dd35e452af635f6cb1664237a07285874ae13e1abd15908757f4de62c3c6876ee832fbbdff93209b8df6092726237a70e645aac7c270b88da415f3db7b140b02cbb5ffa1d332f00290d42c677913bc0ffa22513c90040ce317638b9a21535e4dc82112aadaa5ad6cfa75e0a32d93498108a0c1009363ed7a9911826bc8c70c781424e6f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a89b3a244320ac131d6760e33cbbfe0639523a8557a86d766acf4d9245845bf4b7930a7538ffc446827d3a03c30970a4a6ffa845e3a91c822064cf200aa37d40955d4d0476b11cd0b6ba0d77dbb892443c8456c207214cf68eb035c298bf5907a14e5d24e3934ce7a3c4ede24289ada7ad243f567868d90065689de04f25e4568f627d4371ba0486026defb75566c9b5278533b4c2829536e409ca2670ef91d69bab67413d4c16b3680ed32436732cf3a69c85007d0c67a6a6e90fe211f20b6499db3455e62fec958113f8e72faff63320e6dd2729effe03bf36f8332283f170f3acfea6f932d3b1fe2f8770528ba005894dff014187a3b4bb051786beaefe81429d33c6b3c6103444d1d5466490abe196908f3334b0ac01be888a73d01026b14d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50445d5b01b1e3435fae713348bf8bbc70285a165336a8bf6155bcc533b9dde913fddde82a34d3fa1783ac474b6203a9706f3bdc2f99f96e1c60d55d7ce0de4c398406e350167f175437724a4e3956d7572af3d662f210722f177a2f71d56ec9017e941f2ab509b14b1cb1c44104a52f477386645ebfec8024d0c5d30835c2787d40a0c3212276836635d8e524b9ea3322cdc3562c55408c110566585e25de7462ccfb7e5eea1d77581e2a9161f1603f42f4886c757caff573c1f798272750c6497462df04109c2d4c5bb40b125e070e7cc3d4636c57d7f7003fd7012203b8511bc9df0e306577b344a7bb4f04a26a5c6deeeaaf684a48265ce0de9f0401774d095fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5042941279d9e54b29b5afa7261a47f71bc898805bf7ce751db2f4bb4328d3f675442d7773fccf313f42bae80d782155613cba1877c80a46138c885e7e3242b85148567a1bc329613d129dec5629e95a114c35953f72058056e9271001689827761f7d8e16745c3a395aca127280e3eb1238523e2f38a36b1df8c51b01c76a904b57f9254d83183a5d4edcbd4c32b0d700af627a232417d9433e9b2b7079937c4823e00f29deb9320dc06f2040fdc6c900baf91e12612b0e1c5cb9cb36d67247632f406670ee2f565919c36f27bef234768ad1b364da85d41e1d7beb64b4c378342905e270eb4234120141dc5fc32bca1884f0453951e1e2088b103c3023bbf1595f33bb7721704257c45be733e34abc216fad1f3c0eb613044d48c928a2966c3940d5861666c99b34b2cd48522cf08d233d38161b99acf87b509c0372b315887057c579715214c834cb363f355a44b45b1a03d10829c1dd23c296b60e0dd4852064e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503de90652e0edc56ae436f4295cd1a804d1cfa25b496237338a15b367cc00e1734d0d7e6bb299d56ee189b808de66a4477b58a8202787274dc6e9252989c90f744e4f6a4e6a1ddb5154720e42c9f76e018fe69a09bd82ce264ba1ba35552d6f3d3da6863af65f0860420ba42fcc375c0cadb4de697fcb30580c865e43c0c3b626ca59d136d18ca11c4b484816efba9d426a66b26adf76d47a33c07d4b260af20d721ed6632e586a5ce14483104fcd3e2ea7ef56463cf520761786aa063a466448ea6220126ff6057d68a71a1af9a8890bac53f23ab7a33b442f4f2b540e89bc0e28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50d0aa23157c68a453eaa0b94cc281e901ce0785573e758043c174a87c1ad98c04d0642a62f4c90434065fe13d99e50c5297ff9c2766d34c4dc3f1294ac558942cb82d960c5392b3761dff5f726779d32ecf2d675441c1db5860c954517aee060c756b3d3afa99f13d9af3c837b65af518db9015785b6e1c0d0765d744b7d0a15c64f4ed215b2f2708718b3351bfc92a277882d7735ec5fe1332f1de1b70ab190416b57839b4c7bd4afe4b7519e8ea05573116a65cdb102a4afa0a7b5e62b7fe4845843a519b62f12273d0c30a4ef7da187341c010f21fe1008e20723a3a324650446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a505d4e486ef07bf07a87e75373f7116f6d189ca8607538743292f5db043dbc0276c9adcc6c97e0f43c7f7761534539f11b179c2767d89c85038534180805c35f1abe798c12a3336b12f0b1b5520ff091205f8b8642f0f3d86d67dc9e1965984b79ca611c247ee03765afb39f2c49bc83240dbc444d88104300a05aa7136df04e1c6aced511e9e6264d07d0ba30495ce3452e11ec7abd13cc3dc986a32d25d47c3671bbb26b4ede6e5ddb58f87696f6401b95584715dd302d4a64b4e670af6874056213e42bca182f3ed167b71affa4511659eff469c1cc0f62191aff6bf75bf743340a843391cc67266afa64201f4eb90d6ec1c407a8a60d4876944871bcf72137e1ee1d5c2c5d131aa0c85f3d96d4171208c1a46918f71f74f5a1930b35c14d4b61d52851f75aea479f5aeb095f10555e3133390e95d0bc62054d896fbbdb606bfbfce8583c2ddc6be0d574521cf666080ef10f62bc2f4e5b2e13d361fe75390d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3abd5961296eba822eaff199445f609736bb91a138cc16a97a6357fe641ba19b4e734d8466a612f94625f130540b862c6ae9b3f236f7698b6d66a9046de8651e35b575bf55e8874659cd770c546e193e0b64c230354130760a68de6134dfc89e113bf28448ff77d60f218baf5341ac6517d5f7907b09499f604d57647c139b981a3998dc59c01b0b111e6cd657adada81e81b0b03172b0752470f5b96f35719d270cbbc154382e320e560a931e4b3cef5653fda41bbb5fe87cb08f1f310ca6210e27b44122b97b836c0b6ea86343a0b85f7a871010f7b4387ee556755f371d90354c2b5323aa67eb1d560efa0fd1b37a5aa93fdf1297ce9f4d8d753b045ee77f0451f6b4392b1fa628cdf08f4631e7ed4ac01b690963369a43f1ea9c12ee3c2116dd2eb973fd390e088bb9a211c3b1575e1ec65b3012b6e01d07351f070ac008323ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5018c8e56335d803751f23ca1cde67dc00646d2c132bc8371e39126d6f35adea41fcf0bc364a1b637dea2c7100f7c59f50cfe8dd12de7c0e4b9f3b2a731df72f45ff36e169d21ffa60c770e45ea20c9710e341dd5e388dea6b4ee6db3c05053946837b2868647067152ee7985c82cbc4775e9471773179e6282e5cf95faa2616073ffaeb644f47e06a3ffe904173036b2d3bc72c55b7e4162078469a6e8b988a77aeb3d779bb91e31b95cc6e71e0768b2013d76170db5ea956ee809f3771506a0df2a8804e8104ae35f3ab431b56caca17895de927c7cd9c1fdf2a8d290c99814a9699331ccef5675078c4b96376d3e0244ca5725e7dcaa56fa72072242e4fbc25e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a8c728657f00af3c23f6621f9513471d3fe5b439053ff36dc56dc507f7c3d21fec536d609a4b5e2ca744ee00b99ec479914042690bd9742c983e85385e2e4317074568587f6b592b596e2b44d31cd9111b7d5d4d7fd7430839c5e813f672ee024dc2581805f2d03181b61e0b0a5da77093c3024441159a0dc97bc907ec053828ec85266754add34c35dd96510bd7d618582b1d57a180e125baf62a5bcacce34075eb5b780fb72f6f22b89f29416f8347ac1e4f6af764d3603aecf613a16d6c484df5d863bd60c92f2e43c40ec9508e1961773b36dfe8302bc6d29a1d8f7d81433e2d620f86d7e052cdab786b2cb80824afd2814c897e201333df954e53e2594cc81e300f234cef7c2cc4c257ba10eb28476e8b1c3f7c534c7b791c64b7f6d66bc974be19b4e7a805299c1628a9f7c762b4d70b3851e5ea6220751e5e7d33a72f70bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501b9d06487557172a9ab2ae1fcfde624720fb3044774c36025ec97b744a25796a34d8442e5b7e62198092c74cab72990e3070632a9ce71c5f5bdc9e04f9c69127af7c011220ea2a4a0dadbb432330c07e0b0661214ff44952277b883ac71ec80ec3297042e1e6ab4f68afe060be9ef23eeb041a16ccf901409cd7ff1824abf67e9790ef02c1e41969b9af150cda73a42fa092e466a1a74f24fdc5a96e32c4ec18058fb02a3acfd5210ef01645cd06727eef52112bd06dcf42824e1860dfb1fe017ead87218cf4d24413c90e4c1e31a63bd7a5375f0dd05e19b6ee9c397814af6287b4ad0bda761c738ff06f54e52e320e7bb89c44f7e6784bc1593936c8fb997be26c077d10d728320cf9c7797155a87b1ae8362162f6477d1e89ba1605ef83426966e9481f9e6f50f60cef04cec6c218c563630750c1067deecf1110f37dcd5db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3ae1ea7c1c231a8b33be9353403455ba7233d85841ebfb561093502342a2133f24c9707e421fd8792be8a43867567a09192bce3a4303d73603756f022623848c5ce8d6367323ed4872f859ac2fcb9dd618295da56a5f52461abd86233217e9191dcdebaa0c161c3d264883fc115b2a0364b26b3a3e57080769cb777f78f4dd3f477c28b8244e8d9f5f05a9197379b4e162d59f44552d4a5962fa4ed6024f030f451c36c81177ad9b230143de6c49e23a304e03d831cb54927eeb22bc440c2bb21e710ebc1df330ea79aab0762ce01ac525accc5f08fa10ff03be2b74128b014f2a7d08f65787d9d6381126c207e43c5e22a2b86376d1fa096bae03f343e1ddb513b1786d7893fe6c5196396625211c6a195b7f5803653fdf2f67d21728ca18cc43c287090606b0f355472c9e3edeb4fb2877fc2024eb0126395558db454a8ca80eab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a0535175f7949c143e58b911af5b77e30015bb75e048e7944b25bbc27da62c2609b726e1d562fac29e57203494df95365d9f0336d7319a143ada461421f83e64a1098714d104f546da5ff0c0faf527a2db717cf067a4d9c0cb37acc4677f72f31b447b60e2571331736647538c0d6366601492e5dbbed2842df3159381f1d367cb0373e4db897c077f2e42835bec956410c38fe1c9af07401d5147e4d553aa37214e92a33cd9090454d41716c71811f5e71151e6641507f40c8ea911b4fdff948ddad784e048c9d211562f3620bd5331d3a8b495a017b1a2c4fd427504b999b719df6b12846417f2fac86c766b8dc8979c30c26017029d805040bab693af92961ec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a5fa51e3b62997a3fbf18e7569b5cd4124fef1a09a6ccb57e598b31211fba121c1edda873f081655cc6c8df79de562e2265c28e27cd840c0a47d5120f8d942f0bbd004c77f2f45d76f5236a2e53c12f5b66c8b032d6f34722b898f27457f4ed34075c5c79d8259734a7d89018ab68aa1748cdd3692d63ad67db4c5b06709b6523d20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5052977f02d7489036c85ae5042ac12d5666b1030876f48125663c405a9bacf850e0015234093a4a5bd28b5b30e6388005cf160005bd65e03076456944c10f7b15fddd54481b12ef466f32390556cfef691a4fa3766e23d1627075ef317cadeb24538ead2f8c867f012e65d03243c3df2af8e0b315a5ecc37656909036bae48335aba5aa7dda79bf20281ae0004212161476a66a100edd60375582976b5705631d850994360f363a53dc3cd2051a32c1644017aa743e2800429fb0ae2cde51d04fed172e49672c7029ebcce907f48ec50ada6f4205d163b71fb2c0282475316a43b7007d5767222808c954ac70815e41758bb6f570a0b80452deb77336f94a4d342e56ac0b6fc9aa182a73036ab0beaf47d0d97f4a123cdc3a8738b86dd2e047427431206dd663c34daa28432571befe5b79927f2a983a0052f6d822217e571d1e9d21836d4c39ad314e3a930a1255a70ab7fed504ecb02e16753e31556c95736d698d12266b39b51d6a3ca44e241b9b0391def328a10ea35dbfb7e218f381193f5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a8034392b9dc136094ba04c3c2613902d3e89214efebf8b1c3eacfe04f94dd41910eb1b643a37732731e4242144a53118c5eb572e2c5e2404250dbf1da54fcc0e37d6d978af54a872adc0e507d855652749430f49968b0f2be41e6c6f84aadc75d5e7f34288c3b773e3eed90776e77a074cfd2c5c845d562b097ce36b0594aa539962a70fd47b996d69a9876648ba7a6c44b4b43c5b608f3e48862074e4387e11fdf3804cb6670c073a108a41cca3d26306c7f01cf499b7617da98331d994d1796f4c9e0f71e40569886a755385074413ec03046dd25bcf394a851c3efc479f445e78390cf6120d77cf06361387e4eb6c46fa042f57c0db1608abbb59a9218103bd193d56181be37d5f06b87582168d7dbbba590970f9dc0097eb8c19889f3d12f2afec397db17564384b900a9d66977ab1b02f6234ea1f00802ded34345330676f4e82327d2ea63fb4bea5203681f362ada3df6544871f46afda3c1b3953434eeff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a525bb1168cdbd428b6a3034c1d157a1d7251e43a762ed037e71fd343dd82b14b128a230467482b2e6a12b16f5395722355c1b8417868ac5e28d0a3699bc6685b99d08a355b368e3ef04cd6663b7a595dfea9fc2afa3c1a4fe10b14647bcfcb5367e03a1649554466c6efce0a9b72e7493a388e54606eac0b4db09d75d3d4495d9b9a0433e0fb3a056d13f21713ae701da73b044246848075f056a66034c88c2536270278842bd008ad9dad78490af83be9318f0458c94e2c760ab13b46ec3846f0d7383a3da52f6ae646166ae828146c9d31993b0f667a074108fa15a2bfe80353f98c140d786117906f216077eafa482c44d04be32bb04ada38390205f46377115c643658d9df1267200a3fd051ab24eb92987837ea0a3dab6353707f9a094b70d84b7c0d4d224a7873092e286ef25480ae1b405bf3ab1963e78341d8c36066ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50974f0260d332592e6fef9d4ba261bc102be365466a555b258cf3c425a282e748175afb247ccf2864d9f33e20f2c69d1778a4a42f66b8b353f61d985c1699386e7980f606e676046d0e147f18e2ef55081ff2a909316b5d059bd1a0192dca9b444a2df8208d75b43b5be4df5a9e32b97bf44c1e05ab75da31a7525564228f9655211fa7346c2b916a42d7073cf718070900d27722eb011419523ece57689e3c5062d2670df333222dd3a9da69067ed710246a813c90a00c04330b420bf3aaf255004f225cc787a206f8800521bf112444c32b160ab8903e3cfd1dcf486bb54b0733216865d0a5c37e4796bd317802da2d10f48f59a8cb010ea2b2781467237f0548111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504b505c7c875c80362b21cf267454fa5668c0785d7e6bdd3924267c59e5da3020914b757673914b73e54d392876b5f94967361d19bb628d5827501d713dd4fd5b1321e27d6fa6ed713e7c7b4d64641105b984652478a22349dc25385ac909540ce4d25f5d98e04c0997f65b4e96127d5f06b9ab120e895931e828d819c586cc7242e5aa65904e763993b59d6d241cac7037c23b6191216b20648c30663cb040118777276dce6b933d906aba4a8d0b74790e91f9351dfee214528768210283ff7c92a6960f39247547c07dbd616c4a8c2bd725fc1405bf0324eb859d65fb6dda596e49d17b02ae50214416427a13ad7e23ea315200cb7f9406a555ae476beeb06e1c4c920bad8b537df0a30013c048bb15f48fa414fed2e4616cadb365bc2c750325530164087bba090f6bc7594a5c425e688b4e0fe9aeee2c2d87553fec44eb743f5c88436684b8414b86de38f47e2374f961466f96d8fa2ee1ccc00f6b348e37e7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50c583a62a6664697866230b12d99ae40d8ab39631af319c722462e2364d5129015c900a2326568a38330e795585ebb0118edc27052535394adda8502dbb0bfd1cfe21684c2836a87b757ce31ea4ff751ba227893a745b69059e527b0bd3c2ee696915c26254a5912b9df47b4f4cbb85393ebb5e5be564355ada711d268890202a931b735aed3f7a517e6cce2ca7f7f64b9bfa8c2df68d20067832395ee3a3c173342de105f4cb356a9091a60181f85b31345f655e440e932e8cce2652e62aab6adc19484cfa408d0aa012fd17e68c0b1500932a47f8f74a0311cd5b6afa1e661d28e2d27bf86e59372af3e751a64c4b52f8da445ea0b662135b5bd72bad281428cbb1eb639622932015b17e3750dbe16130aaf549ae8e5912354bff4208b02f0a21a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50637e88382e09f6523fa6fd64c4110611c435861ca32db87248e4264328c0fa627c6a622c260b84129df6fc12778920276b19a02c30918b48eeb44e19a0563545d73a7100104e9d1baabc947d69c49a3ed757ae46e9b9537e072a1272ed77d5790bbb1d20ed7308593748954073f1804fe16064210e405b0c5143e21a6d9e9b27aa410414c80537391a11e016ad13d73a4985383a7a28da6d7315fd0a3df45d699233787406c2cb2668e62c576db40e688fd280105c3a4c4b84a80b436ee9945758dba1104a7ab2581f8062751e3bfe5502850c75ab48db36b956ac09b854ca16f406801886947874619f2f6070bb8f7b84eaf41e9f6fab42fc5e7172bee37300e6b71c1efb1ac551691b8e1d20bc8d45c4548e5dd6ef70552d348a710ef3c9409c89d23650f2db24b52cdc48b93abb0d1b30202d10c550264a922542d03be62db9f44e7c0ace3e719f7c1e476d8bd907f543293d401ff274c5778e1eb773f520d34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aeda98b3f6a6a63477e7dd83b3b23ae2904945928f042a52dff2d822555822d4d1a64a07611470c26d630d33d4117ab2e75b08f7bbce9386e7cd2b04c23e7c858a2fb042825f0215af860a951ebe5970dd56d080705f4eb19a04b031b7d08e3481b401a5c23d75434ef61dd7d265d8731862c21519c5af077f8711f0ffbe1536014a71f2f9df5a11407c75b10d55fb4132aaab93574637347cc3d7c5569772a71b0bbe74e7354da64f903e815ce05e26f12648426d863eb30fc75d84de0cf8906112d3e4e9d022912876d6345253a1679c1f2515151952e470ad0bb4883d00d4e3bd5eb407486ad474dc257596bba07608da43f709fa11902e86d025544cea82c081daf00bbcb5d0045f18f47e3fa701d7f54d71a439c3f06bb838972cdcc141ef441670220176b053e0d6b15c54814036eaaaa4ebc256f6aec2c08676803ea6370bce15e77ec0633e6034561b73a05339d8f5a3fbeb6c041a8560a2a9beb746702ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50dcd69a3025f78c79f76cce17d70c6f506f3ca01a4bf6110415828302ab2784513c471835f486002a27641d435cc5ff0cdbf1da1336e38751c33a1551d8c23430e771547b7be3cd243825dc4445db7a6c0818940cb2943b73f0eba55331081a0b12147307f012b5482eded515040688552b16c50f8f68d512264e0e7d13f01d2f71ab475cc0430f740918af45d997a57e1828b262b5f09f11837b5047848df5752c9aa057109f7d37d7f1a32dab135d5f1f41d01b3d972d526a7f364df825715413ac9045c62a811e5ff0c914280e3c5b088fbb048d5af76e6015995315a75c13510b2a4a3740363a8ee6490041d7fa08f40dfd096efee973e33d8b6ef8ca43575fc2f9583c487b019281921d6f026a3b966efc492c29c73d23dcd227105d907037ab717cd7f50c24df61e20bdf6f1c4d65c4db516fbace31b6ac313d19ab957ac6199360b8fb3637f1e0eb3c84698d39aad6f75ce00629633951116d72078c6d9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a501d908d4d329ba95b6adc336cc5ef8332f5906d12e98b71142d6bb4573cef9f76dcfb025062e55673fcc7e1063d70a735ccf5b463d4e1a60391c4e42bf843cb5baf041455ffa23629e40c17489d6eb071f383835bbf54d77e7d0c514bd7f56810a298684bc6a7ce3a56849f17d4eaeb2aa5ad78245489e42e3e2a752f1a695909c512f30cc7821669241b6b007b1eb3355f0259784689a626df3e256bdb97003f14e92a33cd9090454d41716c71811f5e71151e6641507f40c8ea911b4fdff948ddad784e048c9d211562f3620bd5331d3a8b495a017b1a2c4fd427504b999b719df6b12846417f2fac86c766b8dc8979c30c26017029d805040bab693af92961ec59574941cae341d5a9d1665a7fc21df55da7731999633049fb641d7d0683588ba6da1eb794376f1cfe5672826bcd6f1156047a4d14252427c2fe6071cd3c0bc8897e17bcfeb653d0a9a02d580ed2160eca946169cf8d570033e73954d8297409a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a38a899724af0041cb282091f8ce00702e10b0c244d48ce4c3e4b7b743c45f56194828b2eb475a52cb8c2fc27558ea414127e4037cdd69c5f41157a4998510215bce7863a72dc7614c0038b2c85631c6dd74b2e772f827333510f4b15965da16a1db5d6032735f05f96eff44b37ed11211d68d61fa5cbc02ca42ab24f0679f247d0d5550585b2ab51eeb9a66eff1eca05d7794b6c49921f4791d9db028dd76d245a22b51494116b10073f85731576a70635670638b06c971cf6956e025565d925c779e437d6cf907dca12117c57188e21bc202931be3f474c66cda87d04f816313c772e5fef3d3764846a6a4c478a3878ca6bfb1bc8da037154943542a8c1556563ed0361bcdfa906ea7a3b4ddaf17728cccd9c29f1226e4cfee6d56962ddc62321a3d9180d632f0c2d82fc5044a54d2e3027d06b66b59e63ca2bb56cd4264810ec99272720241110be3f8772d207b2295183fe4828f1ae7160ec6e65b437b00ae7fdeb6f090da27b31ff6729dcd1b233bd31344a6229787451577a1bcd3d5d4edfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504cdfcb6ae7272d042e43a16086a4532c4963102214332f22c9624328c906954e6cdb330054c71508799ab130c2bd015302bbc84243c02b7256f39d736e062e157b7db77dced121766f748524732f9861de9cec32a66a2906d783d42a0e7ce639c13554635c00c4721b682e4f60bb2b5bc3b6c518d985f255606a0c1d55e381085faba94dbd875c158f86ca0b3a5a4b2a43127145285cad77eb4b4a32eed1c33e45d2786c35d48628f4c1fc4d30062a78f429485c037e064e23643b47c0b3bd28f4325022b655383e6399927168e27d5d934ca46b4be2ee3c047f521ee5197844d9d06c08f3882663863ffd7bd269a462eecad50a09770a1c36ca3e5355716e5ba2d25339dfa36a5983a27605dc35e70fcc0646340d2f7b6a3960290fb0a2d8097991cc48e3c638554428f82cf93f016815954f655ec6ed3f37af586c25d5de4173b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a508d23fa02e45f6316bc2dfc78fa86330fbd483b37fe12f7157150d71c72b0052dc477ee40a65c1a265a2f701972760b269b4a6526b358c2574f6f5972c169aa41caf1dd2544a28607d3af59220acce14f95c4fb00cdf3c05cc4180726394965158a2b207715f2ed4b14e08f4a094e001fb3e5280e1f44221fe514144f7db8d275354d1d7d2f3eac728826a01e641bb72e5e512f15eee0f106140c157cebefce45331237136934110568172b40599c95564b18032bf9a875694f93384b97e4d41f1536ce168780714e591d52028b05693b5efcf041ecaf3f28aa1fda5276b9f46ee2298359be7df819c8f9716f98fcee097bd56031aa261511b8b45d58b3b57420762fd517eda4452fbfaff5665036f46ea7ea68311c196231b20138633fd18d2e37217b5ffe48c600b18ed112e08de65c6d92e37d61742103b4d93734f14fc67ee1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a47c21b1abb0a7e61dbd80e6a7ca6da1ec1f41d0a0cd3e7061c0dac210ab8766706508c2f9b9f060646bd63515c07ff7bf44bf90a59125039769ab15213350516736387280cc5c301a291d22576ee7b6b2caefd3a96d32b1bef8c6f672e058638bb1e42709adf1c471f36993d4e234d4da9c6324b6bd87d4a40d03c70db6b0f63b55a68528843854060afd54bcac3851badbbfc79042f9c46690d0b557cb15322f6b2b078da725720f3440a1a802f461e6532d72475962d4a25503f20f0f765639528150e5d293034558f0b6ea2908a634c487918d2ba9113dc31113d7c4f53499c5c9c2a6f308d0a553cab21713a6276aee8466042c543267461294e2978f720429e126eb5bb533aed7196678734ad1f5fe8b616c40e510a6503d717e78ddd2872831a5004e1351577456e7d7c89aa350ff01911d031c437bb152c1ef0d3340ae1f2a32c90c1870bf3abd73c8fd74b113357c83b0ee92c395c5d8a105fdf6120eff6601c9606cd06b2ed731a0042ef300a49155b6cffe10bd4575c08417e1d377c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a099680065d8f6d2a1a72177994544503cc485a1e2e954452390e717e46be812667af1f0d49370141f08b3a441bc32a4bb6b34d39e7c0107af4a837540436dd3b21563a5149b64a13f2f113713d10a56f3a67db1357cb3948abad935f3f820c6f84da6f0c274c531a5212cf2cda84793462190d5114ccd825ac83e01fc5583b089cf9895a56e5710febb5aa0f455462481a2bdd39aab7c04f5018ee348790c06af1fa453f67092e7b2e5ec1072f24877a3b0ad369eec6b322442e0c062330c66bdda8ec7b8dc5a71e8ebd3e35e9ce2133586b6b32606c1075c079b818a30d4d651a825e72a87ddd2030962916a5599564f7565e0a5321d531b02dba61e938e949761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a560d2f196e14973b85d488120093be61246f122e4d79ad59803c62378c43d1213225604b45bebd1a916ab844a8f5e47a4c989e0edce4473cbf70cd45334b0c777095f731798c1f2d8dd5ef15d16f4b013faca04b877409052a0199114be0847ddf54f568ec31f9165fef6f726122173b8378f6070a2fbc587e588709211b223be97a1b44a3082626aa90c35e2691f165fe34e158d68b2708918e971d87c60b41494af83427a3955ca706655e71955c5b2bf5211e9dab113c32ecc46e4ebc1f7d9ca75a674de5ca4726c6690201503159daa4a22dc57fee5b17a6a8725b7e7761a3ba4f6a1f571c5efe685435eeef0b01de770451c7ef701407196c001a885744a1574e5337b0f10f1be95f1be7b50b2f1c03cd3dee2ca300a4b95c659557b3009dad53560c03537a405bd17c14c52a2017b2a63049ee9e27ed539147410b362379f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a5056659979a4569f419e6ad40a0e097263eb080f08a459a3280614da22c150f838833bdc51bb0b0f034ebcab2353089c3fff908411936e520e1b3dfe227d598550f0dd4059ba33284a10610341978a7e78e6e2316ba672d726f6caf637cda79012a48859338bb0a9151ab0421d222c1b423ed82d008f6ece3debb65805dfe2fa20a2fc883e645ebf5cc4954d2156add1237a89234d05599b7eae05f104ca14b422bfab330223382929588fdb075282876f906b562e92cf0f41c7449f4f786c790493befc2fc4a44736f4e91e301aef365977126c57b4084e506ef552274a91b03880c9a27c1a733655f2438a7d3b0fa138326c562f528980304ca13f45eb112b4fd2052b4440c87869f276874faae1037ab44d086b58451f6342334976500e9e70152d850c92ac5d2d4526305abc8764060d9f5049b98a192bbc5b2d76a294d03fab63103e3f5c3e1d2832205e7e298e249cddfc725180f0035fe1dd2902e87e4009a78b2cfac681511305655c6529ea7b8c2f0941d5d46707301b002123d2255c5cce504cbd0fbd39bea4e018e3e0150ddbf063577358cb30a7f57c1b62b2dc3aa9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3a3d68d85c1056595aa77f1e783f624c0c4501674d15b7207ae450d6642124b44e3be41e56fcf6d624abe9ec58bbb507077049f47cac0f050dd8b153709fdd3a1d18b9872e9dfb734e56d4ea06d829b23bcd18a77d65bcac4312573a77f3f2404ca9520c2edb623913a077730ea5ef473dfdabf045cc82ed2c2fe0ce515a375e4ad20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a503fd15f30733ccd6e1c214f5c2007ae4309cfcd7059939c67164b977e77f3b9562e58300826f2335a6eaa0528850dd657b3949a2faf2d4e186297e21bf86a96553bf96d5919e1af2a31c9eb5686d7ee2c792a3e026c61cc46120938111442c1487a3f0a67b4af7b4699b1341944f6095cc7f9c450eb39ea15965a2866279a6e523d219b4316585d5c1fc4be6472fcdf44122e8914b2efd652cd6b6f4c726e9f3e8022f61403a2f914296524584c1cd70ed8a1cf5be1631568bac7ae4ed8ce43104d2e821b072f7c4d2aa8e975f3329830b76b596551001a092fbdd05bbb43ab37446e93049ff58772c933ef5d0fb12844b080a96f2c46ad3ceafedc63734eb125e901217d5d4f852183bd3569f2fb275f32c25533ba23d82328c69d61b392de04d2f2ff50180ccd2388a6d82537edbe602899e27d30685e2cfa3227108c84b31973b763482d314c44f8501d2ad96cc70b18ccf54b927ef8491e47b26dc330811202ac29196231fc35efb7d34d478f8e7aaa12f000f8e87a5893a4084a86842216ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50fdfdc2298f5b700abb0265567f9af1592ad0eb45b735a22c8d16a772ec23c9764c4db838c5ac90761f175f29ca4fff091f7bf247184d594c130c902065f95a0a402034647d4747354ee4322c21b740154f7a0262ea9b083c03980164dfe9db76a9520c2edb623913a077730ea5ef473dfdabf045cc82ed2c2fe0ce515a375e4ad20b163809a44220c3d89218ba11144c3d36035c91f89f22f0c9ff709db34858824f1527a16b715f1b17d321e0ccde16e41331205e4d8b4bd2dc0b122355aa379d8999187c180506eed6710f44416d7680bb7c330426861f270ef048e28dc56a9ec5f708febbda770eee5025ea1313084c240727796d531c303b6d73c5ae5234d4087b183659b1537b19801864f2464345afd86d80d072461ce5d773bb684d5a0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a504366144e7a778f5bd335d35797ef38131622eb794fd80b12487f923fcfa1bb5ac7d02f13fb97b873c303ab735535b3478b1f364fd5178d6bd160b40f4dbcee525d11746bcb62a37b471eb91496811d299dd6ec4da8e0775d3018a126bab9775cc40dc504e7706d634941fb09b5a3a9787bf19a0d0dcd071675f84e2eab6fe419b92abf0e00716866a8ccdd57c26fca2f0b09ab42c578134708b93b2dbefcac635f7a5b5d6bdcd4682c513933b8ae2438370a7f0dc0c4830bf8203c254e91cd2f3a3c4e025e2e39271a05d02c290acb5740abb204c617b033e7a2030d2d3398061a825e72a87ddd2030962916a5599564f7565e0a5321d531b02dba61e938e949761ee8779c7fab097db89c5a09dc2948f2ec8d0a8689ac44ccdd6111aac64470f4cd023c2ab1ff194e3edc07cc909a6cc9a6386841b366568bbbd91fff315f6716aa3e0c16db830ee0b32c50ab5d936d647b9c7841ace21cb291ec62a4a7f82cd34bb72e10a86d3c932c576291f8ba72c4308a58b36da85b266b060158110f647c4d6a19bd4a047040ebda2959f0aa465d0354752baf933e1744167ef12ece64a9b3df055c476971666eb231f131a51d6526a732c4911808743c2015e317db3aa832bb1af226937c2b087452a1acbe73387aed4aaef3ee74c69a9c60759b370ed843ee3e4093f6326729ba74ed5684279b46f37772aa12780df504714898af6dbf3dab54dd5bbe19839c686d2e01c920c7c12123c362f55d2c10772e1cd2e241c7adcd2b3753725c960f5118b786c92c1bfdbf26ab5257541f4ef46094dd26696b972252233a404af73fbe6f106e3e1df324a47edcb0a409f4fe04047a94387e9d9f520c7bd08b7a101b642d740b40568afe9c0e8f25f45f11e97a4ab2bfe50077ad90087d13bc730e6f42031589735fc45bc945878557001373d300908ae32ff4665043bc0b5e1bd5e6e13042ea4b6882eec62b90a8444b2b7974661227757d25d7b53df8f30074c2e4707612449b6c6e63d80cb68ebd3498526058d4b6a52f0588720408bfc12f759aad3b809db3260c238c2b27804037fdc4044ffb16b07479f6a64e145d0d0ef2d2a400586ead12a203e60e94d31e16aba773537533ac7b9ab5466e7eed8d6f53bde66a2b163b2754bf6f36e637ba3980bc5647c7379e38ba167b089a99e64c2aece3465e84e33a32494a027d94440648822147b2693a0822775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a500a3d086e565ef01eed0e090b43330b12a1694b5d36a1d51f791f0720dea3f7104416f617b7d0b5545ac999603621426d7a14d552943d3b31a28bef58cd221465f854ab5761a3cd01c1b71c319063052232bea20391c10c23224a8d21a5c3b2731521f8027fd5030b434a045b72f430316cdefe3bf5dea922122a9d15c7b1fd499ed8db4012a53a699feebd6342f2bd351550d24df8de5a31bf353b59adc7c21421076b66e786e3578084a9396120864f0dc06266cca389181ecbd7082f24b209c47b6f6a5457f84c418e251fe41dd104c4192e11db444c6c4285c937fdc3476325233977b0a70d1ae5267a5f2cb5b26fd6d08d25255132755aa2db13c37e145348111a172328df39d40547144705331faaeee04a8065e27ecbfe2256508dd07cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50b38ee016e519251c8fa9255972cd1c6786b886074e788044a2e8680d6db1a2476c04307b2e8dac78db721c15962a8f57a707e900aa1fd65d44880772b397975038c8387d929d8a57511a5c17173112073887c32c7926b42edad09e72c265da3d85c691443ea7b63f2a75f93edb63164f55803c7071d620097ead451c24674b4ab31dab1fa7793c1c594aca486d5d8b4522644c1437798f594cc71309cc37c802555a2d3919af75412377b13dad56c05427712839311e8b31d85d9c15927e67222e2f691c4bc21d4f9cfb064cd56a006f13fa3d65a29d7f05c097e353d78cda3ecc45bd736cbf2c4f62829c4fa6328f31e4dd9745afdad92685e3fd075b1768576b8576259c84fc3d8247a46fdae6673791068717a945f84d19422841358fb63cfd07083568e881363fad062a352a24292b65b34555aed43f2fc7f0420634d7273ef915210ceea039b84fed6ee5fabd1a5317da46b0f4a1324aea991f67aa097664e4d76dc88b127d7895b54d6206ac527f9586198938d621031a471aca3d4479dfbfed03809a410be9626e11c63c9f51d67de96316f69c2b5cb05a09827f621a22775e175aa4091e8639a4128982712c7e860842e4c3456d4763be5520c11a50a80cc643b6fd8c3d4defba2b9d932960329cbe38ee37622b58471c5f01a5cb722f571f4cae17e34a620c5902d0112b240fd1d52063e316228604fc280b2f011c1aed6c1333c90660da1b7c079713773737e86f167290df5bf9b1e91cda884075c2042172acfe1e6f7f6c0c230eaba7758cd3ce4b3d1521226ce4a00b61c0e00e65e43470104e781df6cc245f100f7653e9d36f4e200efb31aada07309a20852a56945c2eccfdad034e66c523ee4d9d7b7f9fc942b5bf2e14d3628c1ee1b4ba78ee845669eaa9762d3916a674c29b5f71609dea2f6ca9f5767c64e805c705d9433af4bc24c52faa360f8b710b79b2414710d861365c775629eed0cc7057dfd85efa6a10332dab380b73dfb10d96e9582b743d0c5759a1bd34d6f594513de6885f59e59220ebd09f67b6773627409c354c1be52654d9a7326252feff0d35cef93dbb169e2dbf02085cda3a3f48140214541f87cd48e8f7e839c404733531fdf44567498455004c842c6185381cbd4b9318b8bc430cd5b4571edf14135233d72a6708910e1cdd91fc03fd27e94e1f6c8e316ac63e11af9333255871e733d3819b52fb43d20c8c73fd78b064f6406178873eb1707a60ee13514953334011e9a79b02cdf4de438820574c4903aa49adccbe29c7079b3ec8406531167eea030cd64e1601adad2ccc21f735f88f4d074118e0659c9f8f295733734156bc4d3544096c155f23a37677d2c2449c53755e8490e050e8946d411d9dae3cc9fde579a8ab680ce83a0269d8b96c6cb237717715a5f259e5e9465915afec7bdba14360fa0dcd3d23d8cc564c2cef0dda143f381ceee973dff6535d40e45a4b1a6a14361757a5202d0cdf0742c392397980af166a855a7ea7a2aa26247e6f7df502133958b2b713ab113d78ea98e97dc70efd35eb5c36640be3dd71f925a60c8f82e6252bea6032232d4b054f66b4709b1a42563094e806c920ee556360af56c4706272e1e363733f95656c1fb83071e3a77f72302ebf698376987b25a7e80282c7b466c5f22840b60a9753f2d3af5de12787056be86a726d0f5e11fc50414180e4535f5a83341cac95037a3767f765c6a8d01425f9e802bc10cf24f6099f7b4035827d82d77374ec96c406745eb21d8938d03ffb62fa42002c8920fdf4652e180e34111c8a827c8d753c10d336024d11a8a1390a073e44b457600a4653d220f8e9d84a22926410570c097ba06c85069cd41a3f299c305cce103c59db80cf4e460e5d202662aa24d277c327321b7e2e75273c0671e43920a32b8e6bff6e654f7fd07322d70141168316936a567fb00ed316db7b0c2139014df6757632ec102635cef62b6c3ab213ece5aa107c7db25e3f605f12e240d53a42e8245b2c484f479e3aae7c3efc37474e82472b31c136361b41a0179e8cf360459928058f538c4f5bf3215fb997c9628a526a3c3093cf6eb5c44743e711426384e93a2800e41658756cb073cc96761dc8cfac09b1a65d2591f5a253b343627ef53ce549dda56e05ad6cef4b51ff5745a629b6705a148c2eafe6eb4cf221b4611f4b127c0dda90285c02e10fae759e32fdab5d5b8cecce0fde60453331fe3e6c3ebc434f09808c285c5c0f576324ce4270ad1a4edaccba0b881afb052215692847ef03565abccc0c0e220c0c0253c4604f3d093369804837eea6350a0efdb155f5e0f239372f413e5126d141bffac4612e39861282ddf6169e0c341352d3ef4617d51a1337dc252ff087516174b8af6e644e703e6eb0cc7259eb6660568fcb04c800626a46f88807b30bce48e7c9822f3f29124a93739d63ba587443a3770e0b4ae0016240d8b15446258c438d778044389c7b40ca5e7c52d4f2b575cf2b7e7829450e1de89895209835382018e23253b596342db91e3f508994aa224e20360122232c4f04db450aab97cb2af816977251a8f00197b80b4245d51b17136f0c2e8b33cf4959414a4d5bcd3342820318631b20f42a10c54d4815a8ba2b5e4faa1dcfab5c0b7d32253ccd3d346ef1568a574379d0302a508d198842d4406f4e7d3adfd82423e1a21173ee39d92fcc785d55dc8a9c2c2430d521665fb86889bf8b74b54efc324949a614b688035fef819d0e50519d4ba52141339c312b3afaf0a53c47291f7191d9cc5df0ce28171d68b933aacb5869f528d953248901635a2704402642460a23cced72ebc6c70fc6e0cb525bc1651cebe248746b5b993dad79d94ec3c1a5528dcc7a721f401039f0c67b220ac66656f5262e5ecba5d72b9b5982102757bc057f41ad0d2173b92f29fd9c1d73afc73bdfdf15038357b7541392be4a376fc26161136c5ddb19bd16e39f5a68fc7a5971f3b4625ee5ed3157785a1e1f75ab004262440c61e707121303efad237051ad4669a258175b079e66a1b96858fbbe3b42ef25e46b2ff285698f9acc002546775d1203f02f5665d60ab11a2d529aa4f976ca3b7663d545596b74c27f14c076573a4b9ee801eccee44d8c59b1212de59e16a354702926686b2666470d24534364156240f1612faabd67d1aad32fd0e1827cf8d3c041b8279d154f1e975e737240071c910e07e03ce857423c424a902ce5376b151b083ea3e1247b771f334211f91c9fe3fd5d81e7de21807a1113af472775991f637badc69426daf00f3bfc2074295eb7812c9d20531a5c343e5fe2807c0670e8de0003e7d04fe188f357af3d6f129bee5b0e632e654977956a64b5a41a64cf3d0809a65aac65631b6e3b55204b565b9fab15cea56f7c03a12906a5702209b9a7bf1aef469e0786d950249ba82a37c11d3f3666f4f00aed4fc736fb253300bb0a84558d350172759c7d75ec380d1eeed868124fd33f4e9d0cd500278dd73a2814f5676d7efd000bcb9c76fc9c11509195601d5aa1d623086d7b440a08d23dc1bc6d60a0b19a1e8ce5eb47210f787b6db2017254e9e24a66db554f0a78412e61c38768bbb1b73a2762045fb8cb01009a030a7b5ff09519288ac8298a677244d6ea2857b4fed854ff8a8a5f7ed76c68e39674683c330a5df0ca0303223b2c680b540a604ee5251747c4976511ceab61242bd849e060f94f56b6980135bd7352895b8c25d272fc1f5ded182dbe246a1a4d38fe74370494551e9fc9404601e00d9ac9bb1c587b0a50e6e84d760242723df9aceb55779ad663471af86816f81c38f6f1dc3e23d4082e6ad7ac4b138c7d2753b5b412a5d8c134e48f8a186f504a5a840e7258bbbeb62901f147624f040c62e5c04a63f0ecf3695e61120ef688515091b3594c29d45836557241219f437e42ef2d23763d8be36c9acfbf59e8e76f4924b5763631c49d6586e699773a5da909570d642eb1ee427a582fb43816a5914dadc63b36c64c9248c9ff7f634d28e6039c050727038a231191ad1b0dff1cc970ab39af444013303a1c700014704908774377081afe8c786f24490f2ee3156207d9244448117b4e56d200572b8f87ab132fb12c39912e12655d6b4f540bd6944120802c4581a6ca36eedfff6672249c360c66b5484967e155b459ec45076efa256cf471313a4dec47ea3aa725d83de10ba57d463810847875810d4c3fbd10e71d4ad0f67733150e6b0918101ce21fcc2a206d5f440b9a3478ce0a913c73395337d7c4636f1ecc667d679eb942e9ba20322ee9211f991dd35bc7c3c9691a8f677949a47660d226fa0a4d19534457d107160058ca15d033ae6f06da5557fdc4fa7135c3fd3b9a586631d2c7e148ee3be374bf8fd76d365ded705759ff79d1022d663f239a321bc95315f4427e6ed1b2af164e784d0fe261ef13fdd46a596105126dd018fd556cc01c17d12c161c99819d2e70b34b6d45acd03aa558c92dbf6068225ff59c7571bf106e2c1afa520ead1165b7208f4bf2b136510fce9c46fd39627a23db78746f648145dbffec720d57495bec2f5e202c4eff5af8ef980a565ecb400d736b6f29af5a61eae2b8199cebf36737feb55c9007fe147eb22c5ce2b9e812fbd033590476d9121fc4c61eebd755193d38803bc2020c105262e869acd654789ffc7207e8c8084cf004244f43d49e701b8e974e98af441353b9ba0217d4b526b3896a6dd9bdd20e587a1d13a1bcd17250c2952a5a5f620ad29ccd2cb1404957357e4d27f201764f74c0bb793b8cd81105f4234df596266b40af510cd62888257ab56773a8dd151de1e7256df2e1c60f2246785c0bc8eb3541626e699e97901032685b00c84d986ada31bc65c1d8ec5bc807df05620cfc6cefd78369af5cf85fd5fa651e7aa7452b4e460b5f2ef4df445b98dc16258c832b89cd574401208923813e304a4c78407d2a30353e4bbe9273d1ad54634406c648fa1dae6b1f484d6596ef1635153c7d2b5b8694115d3301107fb7b945ca8dc0609cc14a6bf00fef1000e68359b53fe033f485a91d7ca6113bcdcc7760ad550c351b17810224fb906e3bd7cc67b2e9be3b1b811342ea4bc1746189d705a62486710585a74e1afb052ac0818e0520d7a4404b52de1dad305314ea2b210ff305e812516cdd51c77e7d32cb292d2fc9a28473db84d325476278688820081e9f17820256540928025d420e0d89341f32ceea0b8f8f9d0c62e26772ca096d259af6170386d749013b4295262f13745758b6997aa1f03036707f210cad23e82ba4e8cf236d578b634f486a71b7f376755b3d5370f4ee930a19cf0d6d3bcd64646d8cba540f61a1741b7b566d6d93ca33414cca3d7f853f3896ee4357f4ef276b64a2fd2343441a0e40b00b7500dcf6427995432e1eeac8507d094232aa90540890f219543a69fa5236530d2e4c4ed37378bbc56e7bae025b3214e26bf199711483790a4079596d1b423b190503919179e7064631c33eb87e9d891f2b1f04ce7b00fe736d01744570b7600f56d795f73225f1cb4d1843f54f8793d878a800206c41a2d05a435de44746399d69bfe87c6366e06a67460d057dbecb431c7b61ea27100b9144c072a34d52b34e6e76e0eb0c1893c14cfdeeb7667709a15e16a70c2ddcdccf242aba5b1229c0b34f67a3d57ca5808f73ebb8603b35ce3753e49ae628cf5a535e81b8a92668d4a872cd12c703a3fc1f4975435d26c2548d5ee80cc52a1f0b74679e9f12464e82fd3fa4055e4fa2d92f53e196ea610e55b2399ca2c142fbf60f28849d3a3704a304056cb62b4ad8444c02fd139a6a463a236a07ef1a02252f6b2e4b49ea1913f2f4205fcc920c49a54711c0805d2d0f784232916f087c6871014dafc6770ef2018f288a91ba6089675873a4b60e6c86ebbd5ceada645149c5b0277333975a0133707ee41774555256cf405ff3600349ccad75cd1fe91ad46e0f0d256f066616d2112a8dcf1d5d965e7f2532c88773ff94491d34cec30d7af9aa5baa609606947b7a33ff235d48b4ce080b2b5dbc61ced40045f4fada5d4cb3357147c25f231b92886e2e82484793a1df5455b8aa050d017a66d6ee512910959f11181b8b5afda69776e5243740bd60d773649bc70ec30ebe2084fd00105e88b9075f4e7145544ca41eed2a0d7515230f048888621418f719493fd55666c379fe046f2322290c9d3a0b9a8fa74ef286c13b94a2ff4e4b21d8329ddd3402ca855e07ee834a2afb2b2577f2659f266bedf81a6c829b3ed4131f33e4e9311320d8366ebd08a8274085680c2c9004225c6b11346794c3516b4bbe67fd52890d903ae76c7f1d1f087419a57e4222f018dedadc3659363e7d1b980633cb907a75baecbb1eb1e1dc4144d4b6139ea7746b707b0d08052c722972304805a009cb6dfe29d4391d1a78130143f44363a70a1210cf872191fc94542076e0303ec1ec59db87935aa455e476ec831f62ed4778095339cc2c1e4f436e8d14172ca5ec1a5f99c40800281db86fce809f160ca34a13fa166507f5b28b1549b56f10a2bc980b912a903d0e900d747dc7e3258d5a3f399361f10405929a1da7453866ffeb24346a172f724292f216691c12533829c30a4fd2b17038fa046cbfa81818ab703c46dbb46632c8cb164b4265bb2f37c4d75d45176e3d80271e4962f5fa7e68ee8050bc443f0c86893206eb51f2722378e973f0413200c85c260d0b2a0f14ed35e92b2ce93306bdec55445fb36f0919652a5f17fb1854d1336d45e2ff0c470bbc1366b658116cc9291a79b645e82e5ab48239e3f7883b04d8b23fe4da0e07760f373c0f509031a587f93ea8876f1f6e68a2681c58d12e0dab3b222ec50012dd9547114d0b313adc7e23741df50105d918fc10b2b5036856c3bc49140022768987f37df02de214a3fa8c5382947b3968974322b5155a30aa199f622ea10d26b0ed823b6f980b62cf8aaa626c7e641c4e0bb9412546c75b7710e407153e017484746f38dba5dd481351986b58192d1660592b37c8e8fa143268cb29b4653d551dd16a3690bd35044ab48730eb6b7773897c484cf3f8ad4e2b8535780e41323e37ae82768ad8d2491d67177769862e3f247e8c01ed7e093e34370d445a25b52f483028485b126176b9ac8f5a24df000417c2237dbc64e57e36012e3437633c1aadd47c29f5cefc122a0ea2060f9bf11c9097007b15b8eb022999c8121494165bdd41fe1da8c8e77e855c4b429bab185b867ea275499d425bedd82e55f34200751736904abd4ee2012b90d74f397ce8598665c01d82e1f163c61fc97ba54b9f171df991321ab30352b258a9793113115ca822736417c4346563c5f3697c770878aa139827f9514b5e251446143585b3564f01f64b42af7f13ecd554404970254ea2ea9f4908bc9c36cf6c801a1d787e1ea654a57e0faa256a2291556b53b66644abae3e4f3291440d5479892638024e6ac5a4993066f82f2f67c34805612e5622686efc15d1bad2731a70e770856df3097381b868d66f61571a422807e384835b45d5bd4fbd9bb75d41e8cb0f7eb6fe7adb931d3a5818ba28cd547f1f1b96a758d4dfe12ea7f07f2bb8ec5b0a37bded0f50b2332f86d4f220ef28de453d904220890185733f5390285c54e042cdcca718b70c304ccb2dfa0b4efb9877fc917d39eb3c3b517dd7525cdafafe785d8c79299ba80f37280520296fd46067af08ca249f428b0381cbaa61b47f8f0c9072684f63155132f1fcdb17d2bfe75eb79b1c7ae47d644922ba895354e96e10946e922ae0d89b729ae933122bbb45778104a31d54f022509abec43b0ab2a225c2f8b26a28dfd520fcc8a405af1baa53235d3a6b619e6242c0180a75ee6e65490bb04358cbea0d1e5b3ab53208cfab0588783170230af03c1d2d505cccf8ba149216536866a1c71aedd2c209582a244b30477a70ba09992d0e2e64011fff4a6529c33e10fb15b53f1429a65978336c5e10de35402811360b88a18f4d9aa03d3912968624c91c54474a0ca643d944561abac9205e115fe76fcb670c394494cb56dd22354428a782321ded1b1f92c1d70b15d20b0cd85d327b4b6e02790bcda926ab2d6b30f008bc19fa7be264297eff1a65c18d4d753ec3565fdaf53e2f813d57eb9b331ccdede81a24fd2a313e4e515df424b456c2754a0746f18f2211042f5a0e0cb36a4978296338a0384c62c1012148fe1179c404692ad2651a24f8e7bd158c567b1031e9fa6329ecab1cd4e96826e553625c9068b82dae36933bb20d1210e8e39247ebae2c68fac130435890745bb10485105c2ef76b669a3466aceda3654352bf74c857037e7cb10572eec379173925d8367873f8424128c52104724b30f2513f2b11f9a57adb34877dddaa175107ee3f65128c7e1d122ead0d83a1f518d6548c58eee60e6ea794cc5528cf8252c0e60f649ea10f3c64085d56b56a7313bede4f54e75ec53d136b297e87c89a17de8fef1028edf962d6c17750eb23472f8362a763fa92af3025466650c016520c71f2c24350d31a106bfbfa58e1591b2a1ddae24080c803478ec0ef4bb22b2e2a67238857a0c28f631b78bb0eb0175c310ec7dc71f0717902c4decc4a0baeaf2d4585a748d20b2b61a61fa404e0de584f4891d734f514ac05f984c02a87bade3da03e886d3299dd3fd98d1c602959285dfafb3b6995f5be78bb25065aa7e3be314a47bc1324d6091cec2cbe548eaee2649db3e545320d461e16597b65d137ff4c4e45027d47601c3c07748c3616454859c6fe873f2a9ebf3cb9ebf3003002f50e248f930d78b0db200a53a04b2bdfb731772d1233dbe8ad7261fdae4de6950a725d27ed38a166552c63c192582df79a7958ec9063e053a04414fe0e761be2da4a105ec03b0ca0bf458cc84d6da1cf2d603dfea232bdac3600cf87b804c8f1fe54fec0ec09acbbed634d8e6356a3e7485c424cf8149793ea4d47e38725fd21c07910fdbf74323a2e4f0e9ffa29d42c5540d9b5f80db624185e3800e9652caf3f6bb76f7d3ae345922772b6a4031b9a33359e438d12dbf11847cd9ca81620247d4c9a565020e4adcc213f7b7809c0eadd4a5119435aa525b65a0bb0fd62c960e17efb9a2402c2d19177acb5c4439ee9f63470ae771595f5c049f1ba5a1fca18ab17e84dea52d305fd1f693bf200bdb4e90f2edaf473df55c56f5ec09c704d8bb952c5852504632b7837e81427491338852d2ee98e169648a70d7e5c9c1a9bd2270a40526458b72983360dbd2378e2945867884f524fac3dae39d3d79e5436441a6d5424cf2dd613dc1f12f6975bf323e34971bb0d33a7def93d9c928174a4ed0917a2b4f82d0e99042eb403cb41061470098192f92ebdb0bf1880cd9f3b72b5467dba27987c64bf7a6f3caab6208c0ff95b8d08aa3513a1925478f5e5019bd5287c409dcc2746529a39211ec92ce0fe243bc1d21a406f63d8468a5c9a603460bc57ffb0cb578f66057da26a300a4baffe0426269422c42227056bd8db01c629a00c6491575c64814f455d8cc829ead2fb314445ca521b4ad71311d8671cc7037665515b061fee230627f06bb072f55c97121636fa4bdc4543334783164383ecf52b99fb726b0c93cb20724cb374edc7cf2a1180863934a97e1b0be0fc7b8df7076ad0885c56a0cd8b515d9208730501fb3ebb315b00775fde1849c94378e54c21732ded0e5e7940987904ac9e1b9edc387e03e07c60413a3c00054f8d648967e051f287185455c62e1d2f0e4b1419f8811f462d03252411796fa090f96d3f15bf79c5041d20fa3dc919ad9210783adcf61eff08c6302dee90012a5b015b5fc3720abef0e1142a88675e76a02c14d6a1db603532a9163c07be779d2e221614c8a65fea45a418cd377c5899511242e7d8f973e5036569cc4be476f53e4236fa82535af6fd0c1b5dc5f0176183144e04e2b0322902335cc9165520c37f101332a77b15bf89e37c27d37007abe05b5dbaaddb789c5b3707fd26db5189777368792fdb1654dc94790e4855289043272dc63182389d6ed92944d0ee7161c17b0f7981645e9c3770267ff2825cecf9540819b5d67849d414307f4ca758df6ca125b20de8484fdbfc599e85d321a9f83563198f2a7a1dad4b324685a10ca288971fc003b56e0cc0cf35abf1e14bfd3a1364d9a32925137c5a17f757785956796b634cd73a4f13cccf6223ba4a69dee4d434e781553ac130b441b444034774f040776ce2ed3327705c13d146612ee39a8920b79e601b3dbeed16c3669b7828b9f351c6588977ccb1286db5025812baeb4c0cb1182065a4709914adad305a9d260d3b6471b23494b02e4aec79f14f08dcf933995a2866fdcd500019e6c15665faeb17eae54b6f8207a054c7abb54154516d6615f0ca5f3417774201ed044087dc8f756167786366f24750b4a376102365152d6672ba08bb6b3069750d394a5ce9563cec653f72da09aa451d7d74647ff0497c5d8f677da02c4b30e0f8453b344543040afe4f2e06cb46645e7e4651a4b4221292080d2f23e4747c741dfd0fea1933634a9c711172e152422b22be21651e25725f42781511bd9327864c481ee444b34c1b2da4136bd86a36bb75f3479614b410dd4eca5028aebc712d153f1d3b92bf06ea78493db3de1f3fc8384c35b29f641128d40a7aa71f9176e2cc730391cddb216766b72694cdd20d868db15b8d95cf1314d43c38dc6c8176bb62466db4e8e72557de5702b96c8306230bf3346a34a40289186a11f07d353f0e719f3fd858037070814e41facfbb4f54b3a219feb59e0fe0a030060d9e894246277c03a32f423d936d092562a6bc560bd6543822f7c544a35c6c78d8647b6a3858e9520b571d4aef748a1349da6c496f71c1677e453b5e238c5b3bdae56b7db277737b9c6b6f0529794725d05a77524e6dbe64f59fdb7c86c61f764654935718e3242a17dc5c085f17072e8dd07c1c75778a14d6a813734537d61e85e328631da4bf26d822d80b943b314534a54b506976ff196516bd092962cd16339c3b0a60199e311addf81f3cc5e937fcbf5b47b9ada17af8b6037b61a0ce60a2e910475b65df02fbc6b23533b63a01b9aeb335e700a56a0c7b50129b7637637858261aafb15e378a758c2bdab8c16e87a9ab4a19a89666fe00543accf89f5b95072d31f72bb46719d3372054be665d9da87e2e9b6a4b0765569463bfa3dc3600173c60f3980638cd5a8a069f2f0c71536eed02cacfdb451b8b4623bb7cac6283da9450be63c046fce2a467c12feb07d74ae2406084ed05d03aa15b329dd66d3ed8ad57732d4f5d75ca4b29736ec93e74c6a73be0377651865cfa3643c39b578666fa1f6f50404caf89fb2e6741006388a04d025476432ac51ddf3e6cf8a7191bb6746f7ddb8d1b798a1207ac55772b5b98415455539d326eb9cc7022b2fb3be8debb566adfae324eb1d26b414a4f485e0d4e6f99a9724f5486804d4fe1ec2e4cb7012c2d4f4f7792c8345bf7abcf1e11113e2c7ec0ef1f49186a2117d1b13d3f78616063d08a36e7ab2a6db77aa363fa2d9c4a4e5dd73d9b957d072fc4ec73b06db74c744d6a028b815b156cec0a3fd4f6231c07dbb55db3e88674df853e3df76ea94f2ec3e75315525a16a50d2d4c57f76300e646d53791c5ac0d92f9cc542f591064b9674a459f0094174c8ec548ec1c103e528cc86025d6b302cf3c643462ae750962877f0fca156851f64ccb357929a55b8d56d81e98e71d026dd4844349f2ca2a392d5630e89afe76f653e72587baf3614b289f281ccde10a502d6b12b2daf82d79661c70acbcfb4c47b1a21606a5491cac798b385bd93f4489ea1b3f9f04ce60a1cef60d5803bc42d44d542fb6a8132132489d16221f3c034784e36e7708864710bd4c5df630c57361c93843b73c673e905f6967fbfdb05c8942ab2dc38f4e2315dfe3598c968e654d582f183547897d9448c3335a42a6635b310d1b4a86783892a35c7e3aa09b2d85c0cb619c99066e1d4ad724ab0ebd34c24fe9211855973f45dbfb7a41f71e38f491a16fba2bf901dc280469c614b620ab8e7141a8c5313829b94a6f3811d909a4ab5a2392c8d04a1d532671926b937665e84554595c411f374cf91abbc547605be1e827f39ef3538feeae4ba2f9764a8062c555ae31a65e8ea74e41fdf96a1c8ac6332830702e11a04f392f674a26714202b26bbed5a940f6fd552ccf53fd4c6962eb08462f2f2a8bb1f5672ddb007e76bd015e5f3ef438755843741efc507e1c6094323d8eb432279ad30f23803b3026a7b105323979736c6d223f17c9f67e9a29ec0517e63d3a5fc86159bc126b74b60a335e01a444183e610b7dd969971d65a50d31ed644563d76a3f01e3c46d2c0fe18239f5d31f2482291c6c2ac6f466a093aa188cdad954d3856f7bf63d1c6a05075c652941900df791ed6d1be0791e41f0032d8d14cb2e47eec020f2b3767046492f15a1ae1a5ef9009f4943e3316aa1ac425795500e03cddac6558cfc214a7bee9344ac25445ee7686359f1486250df31930347635e152b290647dea62a14ef053e1bcebcf664562d655f3adaec57197590743206bd2823cddd1dcff605424a21472f9b3cf716bb4b6e63e309e95e2e8ef970b48b1e122f062f315a782d7d51dd9001805298614952383deb8b472a7e06d46d717519446562b76548c5f032e80fc75b20eaf65ca927b93fb9850a494307f073e558ea2143d9911db19bca21596a2079619e6946dca78e1534cc18045f854816e34c9632c1e8697a49b5e6253ad5ca079845181b74e60754895c835a9cc47c0d122bf5576a683f1e837d2449496c5a2146e61950d162140bf2baa847f5dad911e2e80a737499a401854a347d59d54e53d216f335e8dffe6a4136b03a7da0e40b5466ed742d47fb658bc99a5e6e570050c7427b296aa69a616b5dc013da355d75175d317a4c4b253759e58258190e032221324e0b75eea729aca83656cf8a34662391e55fafd5380256b4a564f9fcc06d60b5410cd2156a07f4c5114c4dc4b2754551a1263f9f6f13d154a17023b4685e98fdc84c604ca21f12a3870b402d235d79df3238a79eb92c0dcd5d32c19df121846c8778a95ae2094648f76a6ba650561ee454318808fc048886cc6289599240bd0fb7180b91fb09ff9d5d3e4979e333cab6171ac0d47b3f83bce933bf77e65862065004108d8f3f67534a0e5b232f26c439e47b602eb72fd734a22451fdae66b88431203cca887a04893e12d1b85d176218e86a0455137aadfdf0403826154ca92e890c157f2e66ccc442554b93637dabe67d1385c93651f3b7507196139818f03639730620f809d2f7844030963305b9d1e9777d792d630a9464738664d926f8379754462d8a00e501d17b7f41b36bb09c007904a5975d19b7ac06fc5f863b1e7fe67a231103583add2320d6c7373a869173039760557d43661a5c84a17d462ff6624982d6ad552054c91ec2f5da5010f68c24cc8822405725100907d82d302c0156798d3b0d62f31f5621c60a1b7a00932b0629a4154b047bf750e63f691a3519e50b246a3b2254d05b6e78e2a5479104b0633ed00b1cbae04b7e3e0309318b229106fd432665d26e2406c5ca8928e486d259abc89b0abb32580d4f4097782bdfa61f78fc7764e0dae87e17434700564fd439764b6929b28789473fea122b717b3f12c46e7a5932e819026234d47e16b10508cb1cc76511becf540f047d5ca1d9d657d39e1a78c2fdec2d172b71028308b43c3f17cc3ebef1540cb390471b8742590d866a93450c92ab48026dcd312dbe646d3b87f6028cbb5211cea88962bedf4d53c367316225bfc3247284f8367a4d434d7fef58779d467d1dc51fe14e867d4d4e8dea9045cc61227ac597794ed6ab2522445e7c730bb2f316b58d8471fb0b0e31de9aed044a0f367afe30b90c0e28bb170ecdac31faead3474635d40e2d10f37e7dd18543c72e997a910019454505ce79c64b0c03d9f86451b4dcbd02f2090e7bb135ba28e1bb796fee5d5f72b193313840479e4fde39406d257a0a48c86e092916c37235c917e07abb7979046a39533f4a0a651d13b36f3c6a60d3549fb5d9089195020bbf0a5f7ca9fc6b4e7ad2ff694ccaeb7de732d92bfbb7f6330afa7b0b9acece1a6a9a6c7081d45b75e63e4464d87e7b66ffd71b05f403cc3a4a30c82d1ba1ea28ffb5223d9329e564c018e465a4fc481e52be830eb5acc9427ffa5f194d0042197ed1a96080824d1efc88c33ee8d8ac617f11835d64b9722d76411521d60b4f273756cf04ffadf02ced110346aa8bf24846970b1fff31b6435082fa436847500f25d86c6dcddca82170d81059bd64a00839ac19626173b733fb99e7552385066aafb20f227d290571fae84931b6c59272a50f5b0d5eaf5b11afdef17442d54240d3b98c0edfa8396b6b096b6bd44b324dd3d4e27b324c110fed5cad0759d43133cc69385dd2dece444a696c1f1440525ecad9e9696c32617604ceec4a2e81f439f144d851ad91f42b01badc2124488b34eed59b611e206413f9b46a34de5f113638a31911ec229d1d49f13903daae804264760265f1458f15eba74052b9063823a449732869f0b04099bf78073af30358927c8f22e2c297527c33a9413af61020803223187ed2e866b92c436140be656eff93621fc0db8338a81d955aa305234ab8c98d2824bb4c583356520936922300876e990b0c2db46c53724d2de5789f6c80aa1c394d6bcd728d9aa62bceb8d028743bf7727eeb5059dced445f2ea37c0bfec6e24ae99f057e8ab7c321a12a0d5397b54412853925058f2a7439c4dd8b02400fd423edd0d80eb79dce292049887c3857ee6101e4c14ee61d01092550af769725fa1734615d6cd1e0d94f6bcc2362acffe667943f8f0e601f8620ef4f1b1a1e33d41bc271786985ddd47494bd7903536f6301dced5865737e5c62921f602d98e8754bfddb253529cc1001d8e00349644816493d3d3048cbf3424a367ac96fc501d40d6d759e10404f9d09c64852336bc11668af7608302b6a991572852e7934839019e089bf2112989f6bf75a5c6c7d9e28036c083d6236832246ab97ad4b3970cb1e46fd71528dc8074681f9c655fb5c7e0af2a6c4225ec16d36789c9f00a5e89372188b804416a5a146971643052d80d5669523de3a3350b069ce87ef1a7f62b2381ec2d97e22add8472401214717d1941a7ae72a129311f44d86434d5cd420fa432290b1728cc9e828f4a2684f7ad7d35ab5b0bd3d73428e686a5d996263a81423f7b7326c96a1f60ea188b01dd5cba424dd228f277ee81c5e1fc3fd11b4897d0fb9110d7c1ee54b5a7f450f18e577bc341c90bd10a28fbc1c34370e300804df563a463879a547a2709390bb1e8da2281f2a87bb66f0d5ae545ac00918cadef3469e45107beb6e745806dbdc30e3525d76054e7e47e5329845d255326aac1bd0445341da5b5ab57e34670a9e5557654352530e8f298267ba79cc19c422579e577476b4b3596e3e3e2b7c910d2cf6dd9246f7fff049affa516e1607aa1126403d627a6b387dd1bced1df1fabe1b2a500c7785b5e02044e5a42c6787c0649dd54f46e629bd1808ee1b79b71dbf364a0e0554e7470e2a7982fe145daa3763bd5e8203f8161364a6c7fe317f600367b082967808a0d268b51ac321339d2e4a5158155429afa8130fadb43727c65a0fc21add26d68c666ec2c7b52a47d85122d6527f0b1d39810669002a000db89503275e9320aa943f14e148d57cfe8e493ff2be531f5c5e420aed11043116e8536b4c22546746d9b63c0961d946f5b4db3c424e2e520f4aa55264b43c0eacbfd36fd066e80157671143f815cb2dba3ef9100ff4a14cca433f21b20408310283a2210da63d13549e9c4207720a366479e50f26eae13361af64151234af0e93e3cd60df2cbe4a472acf070e5edf0a6cffd96d35cc4b328d574b0af81cb777c24d2a6a8271777ed6a4be2d377b7e1d2602980e76077c3005cdfa410c78e4273ef0f909b1ec470e2b85ab0f8842652885dc576ecb3483614b7fb276a96e210c39e3e442099d135ad45559784a347e5cf143976c05b81916e205421a44b18d530318c608abb6ee7d9f3e96238a0b7a1df8ab0a02922e7c13f475cc35980a84466faa247e0abb141e4682b63ce505f82bf68d7365de360f2795169622d241e623ba77e43c1c480b518b82a15c7d7dfb696e66eb209068443a26b806292c3865283673716b5b8a45192a603418ee5b1259e214782fc916836aed9ccd52f56b990e2724965fdb86f221c378ca616e47684637143c4bbeeebb52a5e0e30a91bb3d6bbf98182591b22e57d0375d3003213c0cf4ce0d73a95ea95ca69007163805d66dbc9766203825367cd1f402702c7ba23bc396eb1911d14f70453ba10ced5b3b6ab8cd56539b0f166bd1d6622504663769dc6f12743b86c34bccd9185f8f7a2867615f6072bb465b3228bbae0e1739c518cfffa7622d9e277c8fde4a1fb28693724ff87d4a63fe5c0db4e4df4e6a682d2a8debd747a9538905db34615e90b4e90104b46c0023303d6381a12053e5e68f34931efb5202b9f96546e2707ee2590932e317d702fad3c263a6be8d5665ee754ad38c3b279870b7607be54375538c0276012af7606f13865a01a36a7c0daaeb684986f61fa5219c2f22733f472e59dc79cfe99e193e158937957b7743fe82fe4af9dbcf1e32033a226e29a10af3ad3c352f9fe905211e865cc933c0279503f8789b0b5b7d94328913609cdd6d66561325c9ae137186cb3f6373215e03cebcaa730ba2e86b23f4ad2e1907b9146127bf32e553857dde2e0d6d073a96624eaeab273d55c1690438c56061f97516f54cf63c028ccc03ded5f3021a484e0c6d44482c452b0004adb48909d4893a41102bec4b08cf847e394c2572dc2e58160dbf29603a8b3003e14e347c9e212f0fde5f2d15ae2d94256ba5ad1e906851747eb889620d39d41675b65608dfe0ff0245e37325ee54df7d1a41c20e566b676021433f6dd090b056636b11568870e92d6d335f4f012ef4467023b955bd59220bd8538c5b1fdcae090105df033e7b1d3bb4a664630e4b9d67ad283320e6a85a6ae21c1b640590a249111d2129d42a6a5245e3752bfddcbd559ce2785c69ea6670a04bf8393d85c47db267f508c3f2c7227894c46f48fac75029f4d923aa07f73677b5f51b0d49a15897a6f148d3133b1ebfad583083ef233e0cb8121f01a19933e491c9426d84125a9e0a29617ad1f5031579201aadd1792135dba45a98e74d124f73002cbb5c0d1b14f5062834619704b3bded18a767b53e5953ee4cec6d373eec22144ce0b0354345f3c06a716aeb6791e2180cff3d6a45d265e452b60694776e31e62b6e88cd1ab5363e5ac10e1534145b3238606e20511c17af49e6b20024a1742423ad21d57cd9f06172126ec06ffa2bcb71b809c84a2a756e656675fc32ffe48160b8b9d05469f72562b485e44a082f81085ed52911d0249e7dbfa6ac63d64d94455bee88324a1333297e63ea3b55026c19a8392d3a1641f13389281d7870f45b735228232fe595d43320342a0373ced6193eb6da5c65672c16bffa0436262c491bc03775795337915d32cb452b870ddb767a244807226f9023339a9a462bf32302da1dda71c632791c158ce12f83774a3d6d896e77016ce70ac8d0e761ff9f05039376bc7eed43a66661b922066905e911cf2dfa1b213e25506f77ec34bcbee63904a2960a2895b270cc34be128f00ec030938a53eec231a0802fc7c08a06bdd1eb9964b7e970a9407f6acb4402a79a55fff5e1245c716c11318f3ba049a35f122c089a125d07bd44d4f31ae4f02c69a14f95b7c20bc2164794bd5d5387326f71c378f264830b27e260fc12c4de84f566740023659494ba004cc51d974dc8c142d32a88313bed7fc0e7f246849a77fca508dacd575568f7f0446880c5deb2c561e145d2d42da130f79fe7e8a6b6324f4425619c046d593a36ec5c5f83be56dd80f9ea6e540e0b6694b6f273208a2da201bcda5e93fc68ab45b2e8f3947282780651670836ddb81f700f6e73a1fab6e534c7c13912217e2716e7f8a9f5666144940a56b8148e2a5be5c3dee42712cac1933afedd112d9634123e4e794436d20fb52e2f078795058536954b2493c43f4db37222e0912788f4e26bfa20644941c9720e7716228c6323506cea2f803f2d7113cd905572627e2c00ec5178e4481ee3a7350191057a5ecea62ec437948bbfa8212dcd45d195f868608a161cd28fc20b446fe0c5b2986dd100de8132138e9b91c505b65fa3654a6a655fed7c93c7d7213759e0c59301e7991645c603d69ccf52a501ec7cb654b23cd59d49a584c0d833b7eb969d524f45668517e6dde5c4ff117004954723de64d1c343ee62c6e50bc5713b3c7a7302492fc6ddca7ca0657f7ce57dcee3c7328cb8c4186bd276f25ee541ad4aa297481b6ad3cf7a0e3158ba0662de8043954dda2ec4fd94f8b4ebcee6419e5da5d75b5d9dc5faf21ab08327e81149fd09261238fb30fc15bf5229df7c274c0a62d393e7c311d709de816fd8eba50e2ec401594736a57fd170f362b7ba65a0cff8269eae67b4522f1c0467d1edc2b6abd7f7e4c9fdd393f8fe51bb0c59560d5d8173f07d40423c41efe549d44473a592eb853990ae6447d66de6a8589324289dbde430730be4c14c92a039a33717de6a2b901fa932e33a8d1460c2081234a7038c22bf0ca260bd5f2b501ce442a77d25d2626c0b9b95b8848471e0d9c165a9ae0ce4b36aaa572b63a6b1e7658f238d2a906447fbde114e3c08a753e46e016207e3406936adf7b59b5b4312d783f6bd4d6044eedf3be6e867c700de16bde6a2924945e4b7ee3021c674256fed2f90c79085e11cad79422667fd06c2ba5d80ee1941650ce76de2a35dba1532d016f4767e25c0ddbb1cb13d363101b0197454465f31915172eea207beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711174337b855d79a1f7a14c3882f200a29244ff62c12e22b207beadbd01fd979fd709b4889277e4ed90329df2e33222dd64a310c932854071d22ea8a757ea89ee103b19ddd14a0e6122e65156553d903e4152a245b6ced2a6f45e1233209a799c31cafd2656a34c443193d8c4b33bef0bb6d34a8844bdfe8971021b2370a41a84461a7dd1b4724eea50a2495ed0bc3096d532d05ba25b5d86e54728c316d8d43751e6e7fdb7e07a974208f3c0c58c5c2796be7b60b47f4b36a4fdbfea70fd0948d52256a5c6da780f6258f8b6e2f52c15e41ac2d4c1ef7b8983bf6afc90d68b9d20a80776a5d6807d37756483b794f94f433390e143354a2a24b286512454ebf7a645633b05dce153c590dbff850d66fb34c56d4ef74ca79536ed6ac272030ffcf74429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041954839a55a2cee97122126a7b25c84d48d477dc4f9f579e4d68b1c92be71a5543eb05ae23e5227e6d04ef9a5376750301a2670b6aacae6952b4fa982770f9841607bba60a931bc326d25c3c6703d2112b4faa250d07c7d96c1bdb0a549cd2fc21933eb8499f6aa04b4a50136a39bf9947c841d2737f37af4df21de11615ffa5158c1c3e6d8fc8a21fd09b2a6c24c4680171a8002ad538c87c168dca6e7eade43b4e8e9f795dfc42615362e220a3e0845aac8ebc620563012709e40a32387e7f4f37f1fd17122be81b97b82a0e2a602b342528fc51943d6c0b688ad00742265c2050a25135dadc325b2d004407c0e1325bcba1a559edad09345db31472cdb868565d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419e789724014f3326ab548f07a4754411c9d3c9a0f294b4e13aee8fa07aa6c317eb049f25b8ea8975ccddba9181da5732748dffe684b91ab1c158509092fc9ed208347dc78f3a9677a51e4cd78b0c6185397d1c25cde9ac366efb4df1507b365215e88440aab17773b92558d3826e0c56d7656f0250750ed0c21a099564aff502f0c68206bc44c2e3a21621f79857fff5605f2b36ee7de712a51c8ea2beaaa3c56ce634024eb204f1637bc7c221a980811ef883325aa66845d5a33840a81da8f16cabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711174e6c077a13f3de3beb27dd2fe7a7ee00810a004367d0ea0010613f1446f45633f50e404cb4592b05e1902919428e8474ba3d8159909f395d50d38c2b63cd203222b58c69b9d04d6f91ac8d0605c81003d41b465a741d88221cf445262a3d510ea07e0e4248194c5ec8fa840e45f6e72f26546e5ce8e0062bded8be559486f56db1fa282d174a6f7ab536ef3c69fd2e16274f7657a60b7762815d7a17a88ba41037adea666eeed5748c12e678f26f0e56021ebb59f5683c1cb46f9e71e52ead2f3543de0458af8c5e3fe42578355135372f15a400130c475aa2bb463d7a7cb119119deb1186fcb211d575435ada31e85e0488045c77685e453410374003f98e05fc4b0a2047c6b70ef09bb17b07dfb56bece20a4ec32a254a3cdfbe048b72772dc024b232af2c7575d3aa7c49de7a6d555a41824875a6337d68233a407f63ca62bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711177f5562646b8dee6c42e0db4dfc6dcf35ba6c486790a8fe4d273a0337b5843c61d03a4e0fcc4d3a5e85353c45f9639b34670cee17709b3a41d7139d034459b619d06e9a259a2f0f75cf42f023b342ca1d937a8901f29e9a398c4d8e7b1a95302a4d264a711032327ec5d697102f47942b2d122e1244e4465be24a50016c523205d37d5e47e6ff7f7118bf332045c70a335bb3563163d3fe1bf6f33323a2601838b8ad442d7bf7336c28d8577c77fe862791a7097706b16b368cd6fe43f09659045fb357419f5b642d58717c75f118bb77dc2b007506e40c6d0c6fbc7853ecf958c0ce16613c803d563dde043b60ab6b29ecfc6763ee67fe060fef294d48f0272c7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117586cab3600ecd2750528c86a6afe8e188f86834dc4a2b07975bde0458ec70b1ba1a44b5f8fd45853f16fb13640b10f5bcc90d21fe7e5f65d07ccdb3b12a6b322052e7d25f327206556a702046c064353bd65ba27f3e6361a374f913b2f060321af3fba7e14d4ba1afce5107eaaa44053a08bd80ccf1e345c3185117bea3c607ed20016177dfe2d5ba9639c0f90ca4e3305be2f02eb3e79001a2e772f46ee5d646fcc3461aaadef519a1978692a5aee7255e6d11c718743043f05d26ba7959d02c18af278931f75188c010a0dbae2f251648dc73e519db3299053602ccab7d94470538372fbe6334de29c462e8d3b71464df4394533137e6b9649af7020faee7bde85dd4cc877f35ecb858d7475385e3a592f20585d4c681980dc9b253eee4b4dc024b232af2c7575d3aa7c49de7a6d555a41824875a6337d68233a407f63ca62bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117b110f46981e10362be040746d526a73b2389362979ec9070a975b844c450267528795e156a70006e571c8215ce8bcf63c45cb60da396bf0e16eb7369e2b12b439bc3c54341cb7672a478f001d414f312008367740a95dc1841ca8c597b80b5363f045c3f8e51371b0c6d8d1fb0bdb57371ec0d2a94f08a1faa1ea4736e670e1602aaad2787d5384597a9b16cf3f82b6e6181342607d0aa6675fe4d27eb0bbc489c465419dda635423e645e5d9d94fb219afe4e0e4f9418160f73f9248fad8a3bc807562083477b1f0ba6670601498765e34a2d22bd5fc45f968a302c662fde799b10aa1369c50e5251c54d53a2f018092ace8253c541a33e610b4a504d2b9e004b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117f440d478f55c672a00a6d83f3cc03416080a0d0a9b50ea2de0d71a3541988e7cdad85b733c6b297259374a66d917e71cd68c702761aaec42e476844a5042d32eb38a5e02f3d38f469a53051d29d0b33391e1d6402611d3590190bd1ead75ae7914719c2448f4f870767ed658cfcc79034520e44c465fa40a937ac9383b2465439f57dc59a4fbcb0ec29bdd5fbcba1b256e8daa267288493e96ecd15c252323048cc7465f87af5705e6176f226b00221870510759190b3760f6099a371ef10c46fc30c436dbcb28497349c5190c733a71afaf436f24c3a2033a631d5ed1317660324d9f4d9862f309223c337ecc184c4a4d4f2941d911857c0841a86550c77f398a748f19c8f4176768feb2372d24da00bdfc050f55a98b6e324d5a593831381523bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117aeae42484872937e0702962ce7ebd152419c657cdf83350575c55c74b3a9c32bec0d2e68df50216adcc0cd0699bb2430889c952fe2943542527a632538b92b3d7844cd3a68e8d506dbe3a50ad74e7e4b05dafd6276fd16484a8f1f43dd398f5437aa3e56de86550aeee6a84319bd237cce32b16476efd665c297d2066934b34e2d148a76ff00306555b00c6c0b093e20fdeadd4be331be5ff4f5d410fc294014a244ee28df88a956d3a677519fa42a2a21e0716961d2646ea325f966cf25a6109402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419513ce434be64ec39a680cf1a118d757cce2080663bcbf7526d4041361017ee6d8e948e1441b381778f5e040c48f4bb6c0f61641760e5e03539c7cb5e44998b3eb7ea3f0efd61fe077a87730aab20a72b55611d74d72917624a81d25b1d796372e5d3450fafc4a051c68adc013075db0b9d7a2323f4b4414e26b9704afb88e479ceb0db6fac7733179618c51cd11d7478e5fae82fa0a2203dc8227b00ea505a208cc7465f87af5705e6176f226b00221870510759190b3760f6099a371ef10c46fc30c436dbcb28497349c5190c733a71afaf436f24c3a2033a631d5ed1317660324d9f4d9862f309223c337ecc184c4a4d4f2941d911857c0841a86550c77f398a748f19c8f4176768feb2372d24da00bdfc050f55a98b6e324d5a593831381523bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111743aa190e9461b7592e6d55030768d17d572bd54e4f2a0b300ce4133e57ab172f728b4e6bede4086d9b7fd642891f6c0b725dfd30622d2d3beb83a87ea3c1d6033ad3c26dfc26877d1bee9a7493ed064fc549346c86dedd6bf1a8e9555b43cf787c17fd4dc9b97231e7b17d4809c8ef2149b05c1bc7bb5e47158e1b79716f984e8f928711fb8c7c18008c6d01f60988771e9e1c375695b16ed139e171accee756cdb59922e4c10a46e15fd75e9319b0799c47366a3c8cbb0463f1390e83856e3167ea8a568808d4677d622f3f6040d45bbe631450c88374007bd24e48b6d5547e9b10aa1369c50e5251c54d53a2f018092ace8253c541a33e610b4a504d2b9e004b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117d08cda0bd6dc907a09fea5163f2b4b55e234e132f244dd08bec5da7950d66726ac1ba5505334544f0c92172df320c62dfdcf1457f6528770eb77f523ec5cad43f5b5d00f4edf1053f5426d3e01b9b80b67d63b66d681335b6c886f0c40c60d159e5f6b17148b491af15e085dcb1de4775003b4553f6f8002a4d31c0348e48c383eeffa2d6999380860c46f10a864a760b6d38208d4dd384f99f7070f3729e21a5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419ba83e429a3dc3b773993d60d28aab47a1347533309ed8f5acbbab7584d835d069d5ae55ecb49a07b8db4e41abe2d9a047b6aab7c175acc0c6a510506f15b44162837376cf80f3029da49440b1583d2429db8020c0904d2110aa7ca441c72d464b9b1be110117692e6ca089712f59140686e6ba331875175880cd3920350d68385de9f620403c9e725323444b7cdb5236f7972022b9271102a20ae3129a49a808ce634024eb204f1637bc7c221a980811ef883325aa66845d5a33840a81da8f16cabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711176e9d16544e050b5c6259341ab729b8384e26af5b34248951c7564421e789c176bed957062a1573232b375b6d72e56510fa4e5b7c6c3e59392e2ee30d9644350e1f9f2a4df01a046ebcead246d571b82a7cbdbe2b96073a53fb409d32390e36413116f65d117b1706f9e0344f3b2f35127feb6106e213191549e6524e4407b311897db306ff924847db83c31f3d10de15f763a055c908a35fb62c3a7b79b4cf52c0505b1968886b72f428f00d7bc40635982f71051454a14e08039e5c59417f510c268e3b939d111529c91e3e8dd2e93ad48af72a79ae5340b38d153e8b4fb250d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711171a7ec44f9cdeca3d388a9c7094e1fe1d925f791b1719957c43599b3a3911b74311477327a1741e6ab3d21257afda1c231d30f21fab3e084976f8b46ba4c2fc1c1f9f2a4df01a046ebcead246d571b82a7cbdbe2b96073a53fb409d32390e36413116f65d117b1706f9e0344f3b2f35127feb6106e213191549e6524e4407b311897db306ff924847db83c31f3d10de15f763a055c908a35fb62c3a7b79b4cf52c0505b1968886b72f428f00d7bc40635982f71051454a14e08039e5c59417f510c268e3b939d111529c91e3e8dd2e93ad48af72a79ae5340b38d153e8b4fb250d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111778074a1adcadf262f54f7900bd44a70f67096a78aea71b2f9b36ca0bc642476262880074dcd24f5855006111762872017f4ad8186227102fb0c1c421f43a9b1df14d78193b05f067fba17649ab9f7f102799e0095d2ff5477b795914ff082d02e41ad40686fe9826844e3e7da789ff1d5fbdd506030f063d0b8daa116c34cd65fcac4f74ef00135eabc610093d5dcc17e85e8a5a8149ff54de952e54ff5ab828bc66981938e2d020d6576530918e3770e62e956c5b371c5f65f2fa3540ac500d4920df18abfe0d287d36ed6f439cd76e602df61fe4948c17a920200b5733a30ec5bb03589d622313c3cbb233c9d050533b59145bd15fe071eae8203efcec06611739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041948b4b160f361687a9bd1832e34d7496f1607d84aa8e7227ea850842fe91067079ff6560bac7c2d3557669731bef5e955782a075ee955710393752d5e26a6667b6665210f1edea4233afff577a0cabf508f2acb561e0a726f55c92e76284b4f3a274043021c04021511f661114a303d6810f53b41294b780b7adcf0294a2fbb525f7c823deaefd72f3df98954b4dd69744216a0776a1c78179adcd24df77edf312efd07409611125f0be7920131c169461fc2f132bc155e2d1080e42909f14409497f0a02ae3700063a95ce0d1925ff082b981c3f8de656222cb4135d0f344240c5bb03589d622313c3cbb233c9d050533b59145bd15fe071eae8203efcec06611739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041921259c479d3dc00da4ee9574f634c71f0e87fd30457f2d0b2d72b50d9f79e67bb2178e42b798d313e279d931daf8b926ab5809031f368b21ce1d783abeba6e22f76e9b6a32c6b77dc0b8874216fb7e196d2a516b5267bc50c1a1e1491e4d44381f878454832ce06d98d55e050dbbc652de398665ba4822490cdfe457c999d60a5318142244e94841556e4b6f93b5207684067d6146e4974854cfbf27467f345f5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f3a63c4be8bef3322f18142296f50a480b198e41b7f7907742e3a84cf9b2ed5ccd89f2730a3acf11a955f81f246a610790fd0c620c219d067858ca1a86ca3c08831f7b3fdbf51a684f50837b2ab51b3f2a5cca1c1727255e2e137a36c6e0ad1999a172033079cc640c21030485f1221263b54b5113cea2045697b27be320b62557fafa78dd6c2022a817a0083592c40233c24f2773f4604e35785b0c932cd95fe69c4a0b015b8d7a2b898667d98c2926cb41602f32f966422d8e671050efa76dcabc7a53fdb8602da6b65564da978e5810e7ba6faa6efa5dfb27733bc16e27537beb351a232d5e10dd3648418028337aa51c7f0dd75d55423dde1d2c267468074b1ff81401cb77199c5573330dc2f76386bdc24a3f21725f2e001a7c4a7e3a1a10239918908ee07d5888e60e2045f2464a11fc3dec275d7c5dc3aa1543fdab18e0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117f717ef1769f31b4b9db54d6780e71d033325897c1ec4342685af175c7464a961a9c6342c6e506f2b7f038210a501503a6bef924d77239d0fe63b337b7d0e3e0157311a229e38f0155341a10284efcb13976ed4205f26be710deba12bfd612765c79bc453c4a4fa6f14ed4f68fdc4422c163c79751c64cd577abc82112294f61ceb81f00a85d3396af7d0d07155f07d316999f47b5cb5f15449b4f02064a7d7172d037d66c2263531805f9674d8253376aa423c2b39f88660a05c440bc249bc58eec1131a854d1e705fddd51c7954a40b7545cc46ab2b6f1ab2936525bd6d651b108a82641d497450f0d83f2c9d296f17c5b1e47936aa0b50eef66a4123a9e4606d9e352eee483364f107e270ef7f660ec8593338cf7379150ac8b97181f41f73a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d170856711178b290747d00e4a6bd1c6bd5f4ef4287bf4ab560795e9475e3a78897214f99753a3a6ef234ce23f7e730cab4e1038720bf2b9761b63dbf215c44a84107cfb9b57555a436ec78b666dcd993e7a541802743fa909741fae0479834a644f4d95f73f77df17673aaa943d3b75db1275458622e850cf743e42ee18638b4d58e0ac7b16742869292d7fb003ad46f01a84018279ed1cd32ee63b4f5f756d2271b347711a5593f328009b9f40b51afe678b11fd59de52c771b1b52430e8bc5b56fcc6523c9502a33b5a1bd2209a8b3f7861f30c15bc918528cdbdb60ddf062660b83dc077377905542c962279d68ff736b11eb20da2463b206ee03051759cd2702b101e642193ae3670f74512d90ced603cf97a054286ac0d4fb4191fdb32ff6c4983087e60c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419051ef913c6824e3934e3046e94e4ae143b5b1c667ea19928d3c9e47d58ae34684e8ff41da1fac757e65f0a6f2874791908bb3e7198545c62ac472d3abafe9e3e2e07f773e4f0464df88fca067bd11b2f0588b9000a0865137217c9354e2c6d3ecba4a70b87adb609d4066056dbda0148232de8681d12dd741a5d3f0efc25907ef86e5971b8cebe23a7295b7256c58307c9144b7b71dcac23e9853f4f1880e752d28fdf372534635d6b757241910e30083d71a31e126e79340247967290951f70a0336225eda4033b23fe1807db32c013e713bd758b227a25d181ed46e51af1788a289c4912b50b5e1cf6cb06205ce2644f1e815375896a0021c7417bd7b344425d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419512c834b0e35207ccb726402b371d34ace570555b6ad5a6a83a7e86147ce8a54cf23fe3e0f35a713dfe050788919d84860aaa871f5702526f2796d11ec5267173869b320b0a84263edeb12595dfdfa774b83cd6a79bc875e2d79cf15fe83db1a039cf33f6fd79f5c55605502259dbe49cff619764928ba2474ff4c16afbd5f1dcd895738d84d1241a159af237a12090546fa53684eb6f30074fd6735a034526296ecd53da3a5982d7ccd5319da5e5d19f140730df97316280d9e9d759cb5fa4f462d802b3b396a3f3ac9313c4237ff7a41d0571e3441aa5956295167683ba848108a82641d497450f0d83f2c9d296f17c5b1e47936aa0b50eef66a4123a9e4606d9e352eee483364f107e270ef7f660ec8593338cf7379150ac8b97181f41f73a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111745d56168f54fc72da1c696290c6d8e381aaede60f97ea812d6899553f705676fd7c026246ca3914def20d966d3b72d1b35cf0844fa0dc472db858350876ab33dfd4679161cbdda5aa05b4d2f0be900743a41e06a132c2e3775a36e73b2418f6e2dfd6c5d946b5a56921f80357c076140b9c95e1416a45b0557702f306296dc7ec7f1fa217f32cc25205e1f04389a3308472c004c107513711202247e6e8a6337a82cef5c9fd1701516da482e479ac534d8f5ce499eff310f9aaa935b8d8ece189402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604197f26b23abe27a0775ec4e069bed7e81aac863c7ec2157e50edaba1090e3278086446b024aabfb27b31555020e3dfbd503534536e46f18c36ac04be5880a12c31fdb51640f9a52e29d6194641ef46ea03650c93755aa92d4c638dce7ba9a4511e09bea2741b30b612a12339119f271570b639b8235aafef7087e2385c4ce33156a0600520dd85354dd99a3c3019b2d22a9eefda223e292360b0d60c18be4f632b02668a2d20b1827ab9f626709a596d6270eac078475a695816572b3e186ef6683f86a844f67c222b6bf00365a11d1b639d051b1c10266260659dc87e5664e00f8a289c4912b50b5e1cf6cb06205ce2644f1e815375896a0021c7417bd7b344425d14c8474945723194d16d4457bd9f7ddd5f9d55256eb7136c77a20541f96d7260c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604191e7094348888ae230fd6e5410bbe0129bd63c732f5a32b775d66204c50b0734966b17870f84cbb40df64a355bf9096553df1f611ce2e8921746c622d9cd7be6dd1421c21cfc2353595b529080a61431eb965a9544281b7680dfefa04a92e7d431f878454832ce06d98d55e050dbbc652de398665ba4822490cdfe457c999d60a5318142244e94841556e4b6f93b5207684067d6146e4974854cfbf27467f345f5692672ba6160566f0c2782e993f2f0bde82c16d9ac141308c5f4143a8603905b448603ee8963d2c0aa62d10c7f2b2579c8d044a4b605c35fd808a1cda82cb3d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f2b7e564cf6a40641861ac13dac7fc189da5d0027f6d3a416733811136ee926b47118754a4b31022f730400f6f10a41bd105a8509f61181035a89c79a715401fae5e6740830f8a314ea2346361600f6171eb170ec74e151da51cb656b25c5a6610f5945f4b87060b3c364076fa59b65b0122752046feb80b6ad6d1405f34074d9808ac7010846863547056193035c749eedc2f036b9dce2d4f15c169d08b2e17cf08283786716e71092bc741a1d37d122e112809896528311b264b31f60f935b4533b90d876bc9072e1df04c8af5203d98a55a5d3cdef37409f6024843e44a7c377905542c962279d68ff736b11eb20da2463b206ee03051759cd2702b101e642193ae3670f74512d90ced603cf97a054286ac0d4fb4191fdb32ff6c4983087e60c92566b4cfe12ee75e924f903eb77e4c5b7268bc7e4954a2e9726fc20e573378700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419c0e8d736bb6e3a131b341d480844c15fdbf8562c0cea134a2fc1b60b115e234661a626186fc6992e916b4408eb5c57061400a253448cef46a348dd6ad024e03f8b7e185e85c80b7485a9aa161628cb546edce32743f13b75b63ebd45c4d7184604a8f420d4318f23935f752cb45eb071ebf6fc4a4ca5ea78e5f0c027e0f21f71967a7c3b8163c61f6315ac7520709b56169fbf0137fde2552759f35e3699fa1af6a13e6000e2f54b12f4b054b695e11e405ecb63eea30c007c84527c6307751646aa214d6092eb109caa1b0ada7e230b3646ce5572756760b228ac5b448cc4175afd3a54cbfc0812995eb9068ab45004ada2a44ec5c5426f56b27a7bb6246e4b8415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419f642940714b21f2f853f126e91974122fbc7594b9d8c2d63c9df627c8de32652c2acac2f1a608634b1de38773a0c86047a7f8f2d704779577b3668051b01366d79fe931cfd426f7e9c46865975a418161d2613162d0f5b0843eae50420d0b3102dfd6c5d946b5a56921f80357c076140b9c95e1416a45b0557702f306296dc7ec7f1fa217f32cc25205e1f04389a3308472c004c107513711202247e6e8a6337a82cef5c9fd1701516da482e479ac534d8f5ce499eff310f9aaa935b8d8ece189402c12eda6273502bcd1a11e64a2f2819cf08557e383974ca94e41330faa42e2d56c33f8807a025efbf014e60e1d76f61e3d97c8a01df48e3249f043610e0361739d77c2f390b523abade20f6fbfa520ee655661247f448d18fbb46b460930bcddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604198f917f3fb66f9333b829774f6e53ff505aaf0532a2113f1602b0861adff40324bd17be55530dda13e7ae5a2a1b37345f1b794b60d33cdf1c7941356dd0449e34ac284356ca2be609dc09355089bcf050c833e2419fd29d04579c537457300e7062b44d73ce06b77ab4724339c0578a059d5f7b3c3728456907b4513b5740941941067404aedf375c65227313d194a84efd81b17d750ee35824031914f313b8591eaaea1f9e62f210ce1082085c3589364a0a935d0118f45859d6ef5c9214384b21b689620631e27884225237577d9e5ae6c7430549ec8857e21ebe7a7b0ffb7941f86f554d7d5d12cb326f2317afdd06947fc4247cf0d8461b2c0a7cf34c2479b0e1fc71e3972b794f6c772ede3caa3223d8c74f24972f433f45885762b0e454886bc44b0c231224bfdc1f0592f9a04433bb867784ecc76c5e00142ab03c416548a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b560419d6f4242a0ff2bf36b1fac55984e48c64602d9b35826153045b5b0021f8eee773bf1b916601bbfd2940e9a4246856831635b9950445988a66a4c5076c36b2d2248c30f00665f5083b3c5bf24efef9b6727188ed24630551237c548928983b577d9375887975f34f570238a9047b7d133b4329c63bf1816c0e8f285761534417258d9fbf651cd0827dc93d396d207c4e59aef0f50046723b19a6685e4dd0d34966a833094f117ba969887a12142ad6a15de6fdd60fd633f24bc8938a40c8d9c700ae69e02eef0ba753d1c645084371bd5c81d6e3279ef84339d11eda152e2eb312956062001ca8837e9af15a1d6d05e1584502a204599a72087c1d4e0b1e11e93d982e1d311357273fe4e0f07367bcfb4bc954dc675ef80c11b9cca02ea67fd550a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117b9d5bf2bbfbde2374a476d710f53254ed590a36683c90f005b522d748aa2d36651c4365ba7991f36b1e8a264d948c04ab47d9a4de437d64299be2c08c5098655953f1652b56cf819861ba02d107ecd7ee52258284ed34f4e4924ed17f7980811a5de665d3189a76f2f48e7220346215a420dff53171317384588411276b6b731a560e250a5a2be5677bfce0fe2c9d44cf6559f676027036507662459fdf7e23eb6f5405a1793ae718990161d7f56df1c0bc2af2c3c742545ece00d4fbf195066b5609b0646caa24ab59f4a7e1b4b49734b42545e854551795f04852baf90c311dca04e1ea86d5d25486ab044026f3540eae5436b351bb86d22fbea6d469fd03a1b4ced35e82a9624d6625266311b7a59a3ca29572bde1704d6a46970351d0a51cddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041982087d11a4b578635e93371352033639346e412df339790c0887fc76d37b097c93a39e488fb4ef5588e0061574adcb67cbdd6e0f7f03a677319f94520613a365040d2a14109b0b183d3e79783d0aad55d9826b6d70f6193ffce53e137c68e87ef4f8151aec93876124acaf749800f957087f7b14f006662778a255492399e3135eb69a35e3ed8424aea35109fc9b5309d4ab070db804484ab937f76b6a8cd57e29c1d311378fb04cd9fa4f093963b54756f33c62fc5b5f699a8ad74ff255d8717638707122dc9822bf95dc600df78265e62d6a1f6d8535283a0ebd541d6b445d22fae6594f5b75150743947a337d624fa4461c0bded8de672db39116fd3b2c468415743f88243d00cb8c83429f79a04772e80e56de60087a25958832b2a4a341429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041915087a58a9d0fb287aa8910fd5ef8d04fbd9112945c0b74fd0ceef183d8ea77ed66a0d707ce4046034e17939daea580f00188a7c58adfa6b7256ce46e5c0412c19626b4d6be4bf12e49d0e2749f3f3047f53b1252b513e62bc77640f2574ab206b412349c8d1352bea0e6b061ee6bd76099fc02fc57a0330e295fa5f7b673044f1fa672e2d0e9e7c5fd22e0947220e144fadb52d7c2f4c3039c945190e689724b24458031739f7273ba6f459adcc1e197f92492c5571855993d9070e2075071c25a89e342a601b096826fa0db5c8e22386a900302b68497ed5b3e81c42e780429f456622e207987e3b648451ad768f6c7390c957565ea821c00097565dbab34d1b4ced35e82a9624d6625266311b7a59a3ca29572bde1704d6a46970351d0a51cddec006f31191034e72a53ced7f8234cd4a3c1b1b78e313fff7e8219d784a7b78700a6cf303302baba6b10a4dee5d513bd6bf1a1e4e1861485ba9583551ce3a9828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604191bd8b533d6603d2559dd602818a58e717d7445580a557c1c14b99c304406a00c5ede876ac008ff1c48244840118b29659565e241e0548553ce4bda7ce3e86e799c270a21a8ace9596c2de478ecd14372b840c85441ad0f7c7053bf10b295065e7ec6245c51f0a32812fbbd2c6a102a32b777f06309886a240f0732317312a442fa7f160a1b43d03eee22b7155d95183535cf0c78d403d42917b75f7221c06a73d5eeea1c1319af44f5e48f2e7a5d5c4c220cd8699e406172dd504c32f2e92c77900070274bd1da23c22b556a4b80da0a46f8f66d81ad814cbc56533faf4cab65d4fdb50f3cef3e7b677c4b7b87b3134ed8eff6496e340671824af7390f70572f7c6fa967c462f22fd58c4c6abdf8ab7170400b0eb69f5f62a414e6041093011e23bcff3c1750b854f2025331073521581989491de792584d98995c6454b93137bfa47d57a274d06e7153337c722fc97ab629cf6169a83b02c5f9142322e90a2b694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d1708567111769c4a84441a87c133d0e666213014674b16198476db8d61481e52b37ef20ed0f6cab674fd71ffa059b23665430aa2734222b923beabb83099a80c624324d0c00322a7734bb38ae231bf1024da578d209e268c84bf1e76b653a80f804ccf5d531fb5e3c68ddb8dd5438ef766937f26545c5db837a5b8098067208a66a142d914c9ae89a738b0f527de0f6246f9765b06518a5d73ef3e84e551c0b951ded65d55d1f287b0de8993841e76e7d757b945613d9d20970e1131164fdb3ca3a28fb257a256a5c6da780f6258f8b6e2f52c15e41ac2d4c1ef7b8983bf6afc90d68b9d20a80776a5d6807d37756483b794f94f433390e143354a2a24b286512454ebf7a645633b05dce153c590dbff850d66fb34c56d4ef74ca79536ed6ac272030ffcf74429ad6025ff719492eed605993bd970af9b148402b61691fdaabd342f40c2a5048a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b5604199235ee0c5f0848107b8d615dbb42dd3ee491ca37afdd94213bb6ea29ac6f9f7de1e3954d8700772e9e707d6db2259c15a329ac7b5355122c7650b3346c868c70ad59726e7d65b1785e7e9435649b42505e1ed07d85eca0289ea2db2f13d9b236eb1fdd6c9ebe20106d9cab2188558b551f7e326f2d44b07d71b2f16a8f249d68ac8a0a0c3dd26954f15844387bc92b239e408d2716b8337de7027d056627cc19c4a9a663961e347bfa4fe65d9a61e75a3ff5c8047cd26f0ee44ac412fbec7134d2a583501ee6b81ba444757ba9fc9e5faa003830a9b09627c99bfc3a4c7ae92c42cb6806ae37b00f528f9a1fdd96e953e9c1a96273517e4e5151015e8d898d3a740057104e8c992fa5aae00ed6159477ae964e30e7613f62370a803901c64175886bc44b0c231224bfdc1f0592f9a04433bb867784ecc76c5e00142ab03c416548a1a35fb220a46af27380273393cb5fc92784366c689c7bab25594af76269129828442c427c8e6e1c85e1001fd0c1325ef32133ac7b1268b5dc10702b56041945484239ce97064438bcba4e52685c5e5221765917f1dd75825f4879ac678608b46dc1708e139551b944375f7c476077fb328a5db0ce3a6386ad3f5a2398817113584439a836512d9f2314360ce9733543ec4904888e3c33f79d555fb4b5fe419f352e667db5b4791e56452ae622ef699b3bd8600492077dca85cb3072f3743d1465641b4c8a7862ed6e0f05d82e7800ba8dda4fce0b3b62925d6a17ff89032e12ab92713994f24372eb09517f6f224abb89150faddfe1094c8b363c526e49446c5a5d732d97b32d3a38f108895cd54a14009640269bcf4c17b1c7398002502d956062001ca8837e9af15a1d6d05e1584502a204599a72087c1d4e0b1e11e93d982e1d311357273fe4e0f07367bcfb4bc954dc675ef80c11b9cca02ea67fd550a379663ced2fc76dfb5fb374cd242c6dcef7f72af34d8274cb41fb52cf1ea33ee0f7762bc495506652fb6b73b7df87635c53985d26d1c65e69aa6142d95eae4d694efa1c2b081515fb8afe3eb61bbd26f9b3ed0b712bb521d4c7d17085671117317b7f5e031d8024c7bd84441ecfec286806507a4e7994686ff6e92abb612870b3ca7e4d6b94b0423b6ee771148307603419a162b423483187bd664ab4eaf40ed9ac2602f63e9d46ec154154a895804e6fb939440619030db71c102aa0dc1f4d3eabe5495203ff0efb917d73b6ff5b6d1ea5e711bccf02055a846e01ca3f8428c2799069897f090e523ea639ed592968fad05d71a98712460c08f61b4a39e6225fd0f331ceb11908f99eb346e082180f1ce6b42d025ac9201aa8fe4ac4929e1549afcd0cebd66b1dbac68957eb8e365e3f1bd36f1b7f473a0f346c121ed5ca38337ab21f59607a514fa1916ef1fc300ff2e54452a609532bec0d2e329fe8821f579bd32bb8ee4e51f9bd792cd6409909ad0fea00c778520b87c43537e205eb47b61c976b397d645775b7315039bb8b0038e6df21aced3a530240c9038b5e867c972492631e4a27004bdef306f0932249e3fa8935d9bb0430f085206a914d7d7def04e93f9daef736c9a4e72f5477114b981b7d35b0f1f85280470e5e682a945860f5b05925cb33112ea9777891d9b57543e3512bbbe5f42c4d02be4cfbea1330938c576080b11518c80b863d1875f450ab4c906ede645d453c89e201c0216924b71dd340b54e6141c310fc3e8d35f57bb959a171c121275170d43d55bf279e30292ef73fe1c68957d9136e3e251062389ae9605e812bb709966e737e193dee134146ea75e9a57e447a957c732c889b5faae0aa4afc46881602e0fd085a48607d502abe18320d03530727e70737c5af0287b64f3b5e315635b4b27c2ca67ba309b1d0d85f362a3e535ac20c591ae54924bbbcc868863bf0184175035d2ec71174cafdf54b8efb5848d9608458a4176516848b310ea357db6f8c75f6284a0e3d1cd66e4e1b295e877a3e97d50405dad32d36281a27b773924ceae75443804f7072ff8245465991772addc8282705d931020a90021c9d688515943e6804e76a0718097d131ff51717116f65fa2aab565410724c9430a2788d7b624c21144aeced4dd2ee2050d289ad4ff39a7e752be8dc0110904351d6571c1b56c5624b1a6013390121a95c38a0944c17d45b45b593a52531a56e485c31980f0eea5924b296f47b18b97308ab535a7bfd55ff437d87d46126d6b03be97cae0bfdff5178ede4902dd56f5213e154f26ec3455e76847d747daeaaeb1077ee2752fffade48009f2c2ba1ae642537779f3f42d67a372d978a2ecbfdff340969c026eef6005ce903e57e23ef1c129192df02bc835c40d3648f1788adfb7a48e96919a8ecb500ae94cd4253c02909fe0f38295eec300766bbe55c36721a705d52d5565907b7421910935c0501610c7310b324b6d68e1d7174c24bb00a066f7172721c0006ba456559f37ea2516f64eb3ba83b185cca0f3740261eb3f2e223ac58697930506623b37021290d8f3d274204770f2e219a33d1130a34b36a5458619d512a251fa25dee46882ae8255b5897d2551d25d57c2ff2b2be4ab7c02c03b7e84a0815eef3670023423a7007e901caba1f7d5c7a6e3dae0f0c0e48ac1a1f6f71eb2bc414dd2df47de072d77c3c7d82f8061ccee00621d22ff22d75ed382bd5f6a21d3a0a535c2e737b6b7a8f4a0b038ef64fa4dc551afa7a764214a2bf5ffcce4804e3e4340122a259412efa866e01c3503ec1ed4b0c9415f47885fe762194ad8a30219d190035a6811cf691232b392eb651f1a17a379770ae5290f2db55bc2352594496bc393f2597365bcd4875556b49322909c773ded3b36320e8250baefed92ee7a2df247bfac04e57f7465f4d4a88019199241e627ff0475c2d5e3a0c0b2e5cb05aaf06d1652f21460a6557f367d3672932ac3815b7453a97665d269e22897104e18145f019d31a73faed3096bf6e244785514e0f7d5537d3cf332711bc8f5cf5295a410653d2419f019f0a8ee5d4733acf8e443e05c946a1d814359e49c9354617353d6bd2855cc4a52004cd52353599db352b54239a075ad0004ef763b835dc388b5024aa7a2b403d6e67f2bc1816a783cb4330da044dcf420203dd15c32083b92a37a646964031ddf376cd10e4599988174fc30561765048a565b8816b39358aa06a8622d67d2e59043dd5a74f7b85a18375688c637eb635815c9bd1175bebb3cb5d3a1fcb2aeaeb441835545557b37a8e16399edc17665ae7251375c307939fe560fbc1884fcc5c9467d897172aafdb27049215f645e6e7c809dc52ff7cf320d31c35481314d08d8a6183e4e7006d380318183ef54d32f5fa3c485e98382c7f67181175b27ee06dbe2c09d8f343ea9aed1bacbe3b4977195c37f3682104aa31eb41a87c941db19ace7a603808714764a33914a8a82a9b02973f6ceb0f3ce7f1b4739e91ed2937dd4f5d495ba52185589a67e80c7c02a0ef4a4acf99dd10deed484e69bdc359dde93f469542e927ba9a7440688cfd7b9179ed224213ca68b350ac6029df6d2bf0944918cf742320c361fb28f58dcb31fbbdeb49ef3f2560666bcd5dc53c6a6b7648a6430c510648872b757bb7ac4332598b83693caf3c7adc5d8e2a5915b50ed3281365fade2a4bbe08271ac2d89178154ea071579bf1408c06bf4d69deed5bd5405753f814405f721ad5769e4519368e0702493b6da56c2046096c3f1199760559e01111be6a0ff44cec2781493e7d5bacc7594f91445e716daf2321d05c2079edce0bb92b2f211dea1f44bc7b6149c3e7fd00e617843eecc8050c8bf7c205cd8c7b247a7345549386156e98da5444f5b681509f02f2484a3b295bb577261416faf50893d0222e1c19267e8502702b20dfa044f239ba284eb0916c0bd8c056967312054093070ff9ef1d36eda78344866a47321fe3150533f0bc4b8c9929294259944630b46069fdf23d20824b99579da90822758a69236b144a1ff3bd060c4cbb4f2b3b705c09100d7e7e824c6f60dd6fe32d329fcd6c938df554f20df017fb90eb6860ce34039f08995b190efe158a5d980325cafc62c7e97721df6d2c4ca99fa5037479dc436c7124047a78fb2d49bbbc1fc90129765dc34e1173d889583b199b3e5578385a2f6e4242def37500bf30c34b88cb735967afa97300a8ad09c874b166617927428ec700393210fc53aa39a77c0c5d993c8e43437735e1567bde5d5d6bef534a73180067151501d673b2243b506e31c85498854253d03b622e1638b90666518304f598e32d653cce76c2f6384007f6df250ffedd7866265d78cb93283b25faff245245e82be4fb927227571b46d91c0e1e64923670a21da04d8428e71972b0210fae982d32c8020d0df82913372c4a164ef97cf46619a6e45486a71d4b79b0373eaa9d8f7ba71b140824901263bc0c491ab33a6b17c14f73330ca23f70e9657f63190dfe74b87f4e30c1014b662736b25cd87b096a422aef52f986dd4052e72e643bb7d650c92360160a51442bec10c27793a8cc1113f4142bdc96951f09ae52411359af276cf70a665bd21215be375123ba36cd195ee07c56a3e654500d36c8720d37592a5f528b03a180d431b4c5bb32106f634145671807bf22d8407666d50f94b054375cc77a36c2618a42f7931317cc1eeb3dcab6f158e28dfe32d44f1376d60f744ba3726c437367ec6fe6707058f196fa0ece1d844017d5930510930b30ee6bcd014bca621c0bea33174c6bbe7ce28c5055b4878d4c7bf4c34fb85579143e3b0945425e88382307b41d13c2ec58f981380456f3374c1453552578dec81b69885f6a161f3720f494c711b9ebaa197e17ed23c11ec26301e17a41a4610d305a9d0c1417b7ba4298065512267fb305bfdda1248ea21c20e9c88620ef3c2a33a5bacb1fa0d4cd034ec2eb662f18c22c40d6184da7851e20945f000b3df6d13eeef3c64dcb2355188300e459f589b8663ca0fb6eaf4f4562bc7f1f0622c92733d706646df090ff2a49aca2409bfb185e01d0900afd022867c073e10de2d51f41fc1ef37a1e774c7e43c7b3594e35104503d36327d3cd575162845b0ae7f90354ce3c68094e29276154a2f1524eb893258c9ad71c9dbf29349e696029a652fb3ddb9bb77ad909873b34746c7e202dd122f76f5e3c9e51385eed80217bd370d4042b058c35b93cba5378dd8439102a3357b50afc3af683343ff72201210dc9ba5ca6e19453ccebff746b7951017770d251d58ae209648eaf4671bcda06532f4200d2c89f670aae38306164f32a49492747173ce167f9631843b973a64dd340a3157a5a6c5988d0c67a1b8cff0b51febd716c4ce97140585105ab52f32becfa9b080907a739a6f8724b1cb91d0eb905720049d64a453a8e32273cd3e56a54f9ff7a82cf97128facad44c634b0790690254304ed2c41ff39351487360e0e525879170f85685601373d30a8b8c35de93696707e67a95d3c317823d6288a663b1e86369901c54215b4916b34ae377086b621208cf56147f7a17971614edf7bcb3fe066b668df06e8e5fc3dd8613848cd1bdc605fdb075ad0265471b315154e24aec70d97c47a0e39485b692c91f2150646d1272d936f2fb6080d499daefb78cc4cbf5dda7fad30399e2a542d442f6386912b3373c7d93862749e603685da5ab63eaf12b8db1378631b3d5c9753752cd52d665cf75e3e651e1c134b37191466cfa963488c731e3e0c9ba15ac225a9531736395e036048792d52af6ae7631817dad3e8315746a711e7b35436df25686a9376c2733e268309e7601100462e743da23d987009200841e8ef4a66291ad251bf6c2931b543487680c471752826ea3c18286c639dea8f3af9963109a6715a7921ef57236ddaaa4d89c8ec5a0e4a54216148c744e2fe585a2816c46f1e31ab16abb4c57e22ba7216e9500b4d5efe607b9fc02606711f487081a3093370c4cd4ccbbeab376413c33603feda310d7e33088147821c1b3dd95652112a0136fe0b3fafa8192dedbd5a4a4a00907ce46df5791ab0bb1686dfbd0b6c513e665d69aa2ad3da421bbeba4667e72c011b8850fd17989ce662af252221e8d14a130fc85843d22eb212d002575261ed4945396c1e6331ff5e7a179b590124cdbe3222ebee54ac33b3257369d948038bd932da477258c5952c5c801a934aab966c2ed88630352f65817a6de2ea3db413397329495f23da026a2ed8eaf514654ab30eb2a4f812d16eeb31312a443c22005231c02fa966ac0dc32e60590675b22be9254d7d90067d0c9f3f4b2cdc3cd0a00c305daac10d6247916c5bd7685ff841052ae19bfd545f4dea12694dfb51c956e541add536712c2d9e03a7e7ec30403f7960903ee12a3b66fd7d05aa8228bdfa651f57974d62636f36229b53153daf00d77001cd3d328d975a17f2335f6166c7186552f8950fc974f226384a065a2d180e34faeeda08e2af6724330ebe4b8dee042210c7bc52c2da6f1c71e46c13a5041b58168e6e0a106cbb06e7cbb338dec2e068efe8501040bff67786e68a4e3fe9ec2a26949568037a2e019ba9053f2e37bc7ecf46ab41e1869e355f838c12d4565735e3882c4fe8e1ef4eec602750768bf92c14d207178a9d7733e2816675d00c966d172e11001d46c144d1f3c743d354ba3aa41bc938c1ebc61199180f782e4ea05ce972da783ffed071c9f0ba0549d67f2a87045028975ca84356450e5b08b74078943561105a847c5d4da94f7816ab04347124d909088c4100529cdd6d32d7351d632da31a449556087225e74b3a5cd5557ce71125d306f104af91b74b1a65363d155b5a50cfb8cc1d157aff255d62e548fc237e18d726465d4bece6109a4a0c58864a585ed1866f0e0d0bf702e83ecb651b278057fc353f6bcf95175a910b3618ad82fd6366754a798aa69c7b90b8511aa6f4f2167ef3e8721e064366de4e5d0fd79ed010026aca52b8e2db34bda0b8566e09f45bf3d94b13d064295d58bc5a097c6f573d5cd8962ea4264640b70bab66c099fc61eb65f94adb54a6375747b65b3310124a8218315c76313976d90928780d3541440b82294e72c9ba082ae26d30e2c8d27de4223d412f32997c6bea2c68d9fa31679951d1628d359b316cd868008d1d7d5d3920064d8264e076300d9c314fddad0abe8c7b5e44a9d8371b6c0328d74aba5a3375fd23e5af990b66686d2b4ada57409048a05a72c8a4411927461282a3ce06dbf04e3f7b49953fe374e2684cc1f40ea9313c72342cae3c39270d2411589504c9eee655eb0d6125750b241cfaafb6582c19461554cc5913a475c31e91a7ef62245972594b7e3152ba04ad610bc4572f7cf2294d84279a6f93110c082167f231e31fa044a738c03ccb98930042e9e249f5a31f56ad1a834a8bc7287c34fae31af931004e7817a6669efb8a50780d2f7478b0ae7837ca05561737b1733c398a3be0b64e378f1f8e3f95ec865b83df024ba92b3d2f0330e92a22f4ef5e72f5473aeff11357a204aa4e0dc9c840ca268a3d3e4ba0254755fb41826d3a46cbcdce38bb48ca29f5dfdd2bf01d732886ab82733a1466368f004664da38a36eacd34c2800c1be46f9ce782a8986f33e08794a653d1d723bca58692739963d285c8e84106af3de07b28ece7669eeef6f725cb70f7095cf4d83d4f5583d713d58882f596310114a56f0ccd04cafdf877560ee25797d5c3a4328cc310d6c583a1b94fffc0eee06182aff3e906b5014454f9df338457200752e20810020f3d43c23b781133b1a14ae4b1758bb05457ad1154e24eb2ccdf6541e8d8e720f6058ad77fc42b804db3b12181d02d7444e53e77537589274da55450bddf3971f90cc6c251f8ca67dc29c15521f2312526cf67611c0367843798809151843fa428da6cf6fe8fde150c583a70c40ae5b02d5f5f73c65db61396324ea6a5e21a9660aff3b2648966d0f66a6d50f0be1754565816c55b054d6249d1ad902f650e31f23260d50a28da76b5c921735f3c57c7575de521de67ed61ca45fdb75c4ca11074307f24f1afc591fad0a496fb536ad0c705e3f4cde1177069d0da7565a533b7cb54b5203381e0171499ec5556fc66736c172253abe1c096b0cf4bb0d33a2742b7524011271f524786793784983ff5e5cfc38f517af883e630a75b71330f5810965eae129acc853382e25281cfe19402a70f4804c5fcf08650dbd347b37b5b35d4047935d1b17137a2b37d7626f19cd273849b979161ccf10d38d9612de1cd27d79c686333e9b466d8efb7830d76ab04ac70c7211d0f0af6f6447083f7a4a6660ecc83554c8009726be54bb2c7ebc312fff7b9278c5f66d2e6c8be101cab4000f6f08c2103acf082e84421e71dabd80787a566a7ba2445266b4cb31447af33f753ca6ed03c00c4f74307dc4125a57190feada8a1f4d353371aadc0d13005d040eb1400766d757be1d0195ef50cc98df3d6d0a1a0d2b8e195117eb3b627c06c4151975446f046c3a73f3d49d4adb1f5736fda515763be94e5393b3c8574ff35e792e81ac051b0a1711bb8bdd79e5035278ace7e902ff3fce46524e7c18c2532a6b29343d50e5f9de0db5782b38b2a22c51c43b8d2908eaca3f5ec9720aa703ee796605f82a2a91d417d6b3c00915e3c5281e9688363e9bfe4163a0ae048242ae3c229e87020bad7267373ae35be1e1c8131145da424a2f895c439a34141496c948bda25328edb4de6612c5562da62788183c946b00df7378404f973012004ca30ccfd8dd267816db6192192228886d0234f942e57ad1fcec79adbefa0d6839b639eae3cb6fe538bd1ac48e204686d74637342426654ba78047a38a4b73ce5130647942e21ac1b3aa64f1de5e6da624bb4a7a96712374e4491ffa464a552bf6e960c41eb717cec4820fd44632081129b566c51a9d5d3f343025339db416e52e843676a7e77914095142ee0e9840bdcfb30360cf4e59e59b54129efa3b306228f50733ca5c331937c978a7e9536b83fa4706b3504608269d3936fb7d8f5cabd5141b00eb4e24e8662132b94fc72484527303a0b7cb3c0e330c1d4e2a970edc9468065b41ea3d6afede156a68436e72aac60174d96b36787c287ab34c42767748453a72f12b21c446740b43c5e375e05e045cf0ca6b42c58d4e1b5e3a537d207a385e57803664651eb60052fc20594e9d6f39c486573b669b42183014151bcabf2a0de8f7fb300f53a22c3d1e4d3ab2a3761e6e78a53ac54dd871416d7e191d2b5f6b52d2fb0d7aa6975bfc6f21022a13b3259d828f4490ec021075a0eb60517312196a05f1404fb10642d2bd764e59a4b371b4c7974b932983245aea8d00c138f575ee81eb09bcb63f17b77c3270a7f5552e58f5ad78c1253d0f85962f24b5e3422ebd25ed23c646d276ee48aa58c403430bd00ce779c248ea38e8e56904f3a90f1e216c240e2ef809243fa38f299a14724c86590308ba9f8f315648c97aa1e9c236214c0078ec4d1f3c4dc728273be3407acbe61c39fe926d3fc1dd3d11dbe0164c0174727d52f7d41a1c3f9c5e2b9cb72ace03c15536579620d9a90217545a127be15151296aa67309cb62da67e67155067966fa4b138d466157d19a76b9b70910a0c1dd07d1a5637e2b9fc1471f146d6409f56f3802ae5324842c214922781c6956fb320904b01e750babb05858780d7c2b5a934f7cd2a7525a55f3169767210e8651c87a30b977728811277710b9024e30600031d7d45101749fab018d29a049c94c84004fa0b23ab686e476ae4f1b608910542c40092a40904fbc2f3b404246674f6e6934f31177acfc69132f48614e765c27122787e26b3d912740084bc112e1869c2a80b72f14416ce14a84159521c4c29a1c5b51883ddb0a364e3aea9621c7642d0f14490b1a8fd5a75cbb05d06a3514ec084df1754fe8a9996c23e72276a3ad285907fdda2624fb4e087cd5597595c1724c67d7493d32d7de75b8127c53b625b74b467883384de1b46c3b538b3f4877fc4b76ec791236cb2b21da7aed040953b24796af005399255c610a1f8e682e40a12dcfec5054b44eaa34f41f0907cf205726d31c0e54cddf0468fd53451052e38244db89c54bf09cd93c25fa651a3b3ba857bf549f5b8107890f3f406f3516150c7629a7ef04db5d4333e1f86d70a7a3720e833feb5357a20e267da0c46b1c81da2e2beb295007aaf6091768007d6ff30b076f4ffc1c5e752d67b83eea660143a820af26311bd8bdc6570a5da3236b124023a5a0bf7a4283bf7c7c7d01449f884a008d5aa3081d2b177e4fc1ab1c54d5355c80192616e07c3127503dec35e85a6015b8971133f1fbb5382cd3e93afc810e75970cec379ddd4c38512f150cce250f56baf4f329060b0306bf65714d267a5a7b4ac5f6443377fd487df636245008aa782308ec5c1ffe787bff48101442776007025c635d234dad15de989744e257c27ab187c35b34fd7139cd682929d803b530145fc44b816400446449f06e00bb0d7af4a6e54d40b55719681b22396ac4841aeef2220b9b03641f1beab80e15e73655374f1f3b02a26500e9202910b6cc253b950dd164f02ab54449d9ef09e7ee9e55e301794e56cf7a3fe3cf1c0cb0f35428974f19260d731f4788a6f1019b52914cf2e21819728ecc6edf35b122a678e031d446be7c6cd1e91a18909352fc17b05c9656f13c1822506ea253f74c5d4b5c6a40234371cc23af12ba31445280e585283f13d91355949b27b04f432877e95534c2c0322f4a225c2300e40b75bbc2096dc0e7de24ee464b31c059125879d85f5afde4f9750586c643673e8b2a8f32313026caad0338814274539258756bef776d38309218e6c1c972d466142e3db87e2a65157c4c22fd5911e126c82a698a8d22d94e6f2a5a092a4c87eda5540d118351b2f48460edf9e2184ecce97827e0fb4080ec8c554842557b085f1a4c9a3535590b003d38525282136cd1d95f3582dc45b79cee4985675e5a216b901c8d8c9c42a113d056d4273c7251d09f0775248228f08e980a73d3352b688cab5f06781754af9749337560893e43b24613e8be5f59d9ae2b49753bed0b2dd88b6e0034fc168ec8125a950a127bf7225529f8b30d4ec4369e6c59a378484e45b835af247d39da47745204b68525c6fc1414c329682dae280f5e8513562d898f9b7242f9075356cfc0787abe28700ef95e2e91f5cb3fc9331946b7591c2d23f7292c6d42ce6247f20e63071d2c6600ce5f0dcc6cc45e7fdc446179ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455247d9532d3c75661126c4317dc1527f1c938666124261a316ecaab54f58e611073b22385e24cbe50f97549a0d33d02e61b3cff7063715b006658c813657b0611781f38c344b73726cd2d6db24a9c88b302b4fe83ef5cac170c5ba1d788440c2136d706304d2e9180b2517a02fe3647377547b43695b2dc5099c1ce074693e364e84de3a75c20c4a062bd7b6426b23b0110cc4484cc5250f168f1262498896f245940da72bbc62b72086a8e12050dee530cf5b7c2835587826e8f9283b46ee175211070029b6d5ba508cade4568b2f9308718b1b47854dfb41938cf43139e1dc29c5237c5e01d9041618feb56f8ab6af72d5d8f163f47c597c1c6efa0b92a78e1724848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552819e9f75a28ed55f8a83274f25251c52a4bad73a2e058f27fab29b01a476ae43f1fcce2a70b0524fbb01da42affd9c7cd7369a491f664d1a0b1329413f143843eeed9547930f142e755076063129fe0d6032494a33dadf1e01688943015e6c637e4eb34226fd4e479a767307fb44d8025c62cc0da44b391961e4220facf63e577ce9561cd8ecca6321036949145aaf7b6e0a1f0623d9a9095fa6af50b7afd754834a29500fa1d473c21ae867fd87c31e7ab45c285ce0323b77f298778aced65111bd89739fa6c3456bd9ec7a431d436dff99e970e63e997e8f79c130da46d16ee7edf24b2412cd2fd1576b17764e065308f51604d2554b650cfb5b4e841be11e2a95993fe437d326d26dfd0602a24d3a7f8d0c2a95c2061f7ea63a7e6de5bd54c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455202e9b714c388211d6a4211534b17e23738d64a5ee8b44429fa8c8d581ecd7014edc6ad2bb372b0064bb12e4e5ff53d377a8eba5a5bcb456f45efb931db149e0a128dcb096e3ffa1a7695bb7d417f7f0d0ee24850b315aa4ffbda612afae26527ba34d24017dada304342b543c063e510adf54a595e282c0cbeff0d67feb8da2a56aca15ba4d4043132a4fb35bf4d85528cc2a418095d50636a98ce2432e1242731cf5d546a541374a5267b7781daaf29a5898973b9be7253f70df735ff0c7437ec9f2356d417dd44f05dec22b2c1d31b8f5eea390a10812352b2a9325468881863cf7064bc291756742c694b94cf56076bc3821396fb4a786f56d65ce02d78390959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f716f06ae0c14aad02fd4931b4fd6b1e03c6ab9f45c4a6bf732a4890f47dce87472da823f15f0e6ea4bcd5dd33b098931012fe3100c137aaa420ec7443dd22a392df0be9262bf490f13f120fd6b49b7616f706ffa2bd127657a9c3d634e4c546358ca55cb799376fb27d347600c782dd76e01489a6f342510664a17ee740ad90737cbd8b87b76750f459aece2144d407b1a113024104ab46600dcc05539d321b87104bfbf0d8102c94400ff8b229d3b8c44b0b5726608545f74af9249113a60bb3636bff43f9e38e8470ebf80688f918019dda84462c5e6647977a84c64d4fcd92ee7edf24b2412cd2fd1576b17764e065308f51604d2554b650cfb5b4e841be11e2a95993fe437d326d26dfd0602a24d3a7f8d0c2a95c2061f7ea63a7e6de5bd54c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552d4a0bc06d78a60428089156083532b66ccfb3805c742c30a9e62795890f86b2abda38d651197db507594b612bdccb46051202224ed148c50b6a7441d3094623d360a204a2b47d50437588f1a0a7f5342c2515441dbc8b04c9a79be29733cae083d143b6320e31e255a29216f95508376d4155d643398c6563f98e109d8ae7261e407b13e2a26df737e52a42d7689843eea3c8827339f4e3e6e295c5b98cada749b3ef45e61e1eb59871bb67c81619348c119e078f4e98455029a6334c1d024330bfd167926c21d4b04f147247995e725bc4444490ddcd932bdbd5a2c94502f39a033a070ea8710313e111a1267fa8330cf9789250096745169f0f94d3843be190959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71aaf092485751415d40b21d3ec8844906f8282600d2e1001cac52147ad565135430425c1cd6e0635d51ceba6ab05b4706f041c40939a10d3d6c37842552c31b0c008ac968d8f9474588cce139c8dfec7bb7e3b055b420ac7a52193e5f50010b0b6425205e607d287077f39b1828ace47776f15d7392b87b4b619f8d42acfb187b14d3504d2b0b0d568d1c772ceb779025d790760fd1b4fe5e2210ea13c629e3427d259b5e5e3d3a2e0d42c962b3c1976272c3ac6a41c77626052d3e7e1c4d1e18489b9231c68282621dd69768bbe6066ec4ae4842eb5e840fe80ac478e89218015af81f70b2fcc056ca1e9c64fae5b641b153c73a1ff4184fbe562c341c36e13379ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f27451863455283793e3769080a1f20d23a5e7b73a35bb1e237467fe6df734db57d3ba029bc5cf37f0b046149514a5100e133fc21bd5ce9e0d54dda31e928f0dd4734b79746244920f175b175031a58ee6b108530677222cf0910bc53e41187297966b5ec401a71e03f4313b346094d3372102081da04ac631b6a6c4a5128e7ba8d23ecc63a71240c4a04dacee34b8a0ea4092e7ba25feb64594b5a124f3bd042d85d6ce9bf0fda599e517d0b1a3b2a96545199300823e849193abcdfc253e9f963557157c42b97989111a9c5136d11ca920647d76a05d942df7eac77a868a036bc6f9ac6ca63b7591c2d23f7292c6d42ce6247f20e63071d2c6600ce5f0dcc6cc45e7fdc446179ecea2b5eef40226953c06791f6d048cd51522069c1e94a91885f07dd97d3151bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552d04aac32571d7b2ea22d5a7aaf33a70dc1c14d557b3d714ec946a212eb9c48647ea2f142672d871bf30120225505204700ef0225a1d4d17a80949d5c3e70e046d43379408dab55758a8d773a5501d752494fe639ecb5f71e320901000a613b19a8786742f81a803e6936b54eba23e65b45b0d37eb78c0c746488ad11ac89147d2ecd68418bf8d50188d8b53d0add7310d5d85e13d8cae770c366df0cfee11d3c09ba53057934754f6f98ae65463caf4c7ab253524fe7cb06f5029869ff923e41bbe0396df87bac6e511eff2045cbcd0ce63d216a50c8fc507ce2fa311ce4400e059d4f3ecca80f0a8762aa5093bf85795ea52263d2fe031db17f053f82f62d7262a0f00912b8a75732cc241b9c82b167fb2b2531111f897d001783078b20e77d2d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f715e025417572ab11f786f3d3d7def7649f1c78a3d025ae135e2db174c1466ef03443e550c8a62d934dac1cc58148b38219566386b87662b64b931bb42ee63b221735f31585d82236c847aef6fe4081a4532e095615518726c2add860a8ce0c9587a9f2232509492516e62866a9af85132ca38bf5e9425b92312247f1fab3ccc0f27e0c4766333b56e18e220753b5d73333ad3ee543d8ba65e38afbc263407e30b0f42a266f6a42f4d51ea7809a0ac6c053f3d5e1a434197072d90fa437d03312feed7063549881402555af9578ba200441dc70b567826bd2b0095e471beef0140edd6b62f26d7ba449f5eb17b92d6a1739c430c15a343f80db6cba35f3770d106713b6640e56f9d3f42021110626cda0d18b99c2e71e64960cd8daf0882280675c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71c5dfdf5d2ecc032d47ca4268b05c7562b4e3c85dbdac30223f6b063fb6d40f3d64656d21cb2b4e5d2236763643ab485c68fedd1074189b59d92223596a7ec26853ad0c1a67ed0d621250766d4c3cf31a8411ca32662d7140fc5ef73f0aa2877b708ba519c24f910d597c0d19cd001735a9376c0b27e15e4400280462e23ff93be120c3222b242863631f8e564fe9eb4883e00e78557a02759b6d541b11a6cc44abbbee66ca391e0b0d10a85ff461046574de896046905a685f90b51a8879af7ec35b29743c302a6df8cccc650a0925144b305151fd55a34de4d3e438ace77e7ed3fc8a4c54306e71987fcc114a9c2d6b83ffc3316e7afc5c11e4473c5726ce00aefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f712b603e4d66f1f74efd5f9c7c867c6776b3d1305ce1379c30c94be5148dbbd91d6989fa24a3c1a41c318bd0710d6d4809b398cd532fbd19610b5dca557431735bd38d9c709104b16e380cd15e36fa450ae50b8757461ef350c3fc014e8db27f409312e121fe70ff474d4f4d03ba574a46c8386612b52c392346052c4e3b4dac4b644a820fc39ce54971381d52b13b082cff369e284a97b404eec352743cb8083c3b886e2a3da06849ae2f1a3c10ea88764af92a0e57d73b31d67b6d291307cf3de9f6654ec983ae26575f8d0d58670a70242c8d484766ce715a9e1e19e05344396a5c372bfc35f00c1f8dbe61aa811724b4d27b0bd65ef33a9b6d2c42a4f6bc7524848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f2745186345522cbfd111ca4aa3654ea9342cd60a417d94a6fe0cb9129854d6061b4c0cefaf4ffbd92211dd046c5d1e4fe745622f2632fa21fe429690544322ef4866af6234484606094162391019b9b19225cc74512822d93c1fffa7156744c21539a7d8812b658a127a6c2e975fa027e87b4063b823d34bd158df475441097da1443dacaa6a4cd2d37df14d3f70bcbf28147cd6c12413e91d2b5f19467bc24c7c58d527d50e6d88cf7557c4fb70a00ca72ccc2dc813f89e4b6a2533a733335acd69b624824f31d05a2aaf0db200ae910c56bb32d3065517024838baf56d8b6b00083b863400c130eb47d505fe1321b3c6742bc896363c4fce16c421685cce77044f150c4e7caefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71ee15f40482fc6a64e78c724cf11dde252b3fec355720796036df53675673333456ba0c3a23624e782948f76278a8b40f4d4f667e6f1ed558d6b29c337e0ac47e8c802e75e3b3c34598f1025bac52941f041c184d28335c5ca24a124242f361332de6df28b2117c5de204eb0128956e02b6a421051538dd0ed85ed321a216dc00e6fbc744a4f85a55fce85d3cb4c2e55d3e209518f5faf213cc730c4fc733656e3f05d72778ffd72160dbf976a78ef9568aba630dd6ce9979642f600b02f4d57538680d0b5c0a1546ab44576ca5aa6c722f6d505fb0fff82e7c180358752a625734dc7b59c9fc267032ccb401dc1f344ab3eb4702437e2e70d797253223e83d031bfc5359c36f38279213fa195271a24ef08efb4b21e895134004841b6d2b2f041bea0945e434697c9ee1e40e0a6b8e6a5b2ea319c64ad601321c0e333143ab7eef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552c03c971072a07277d0807a26e77cc94cd23bf01458faad03e64c3a4418dddc1dd20498229b864c224de32421c821dd0d62cf121c7f8789236936b9587874550b0386f0323d704c5cddfd67126d353007a5a6f64dd1388238d578ac40e6d7d939821ef4539412a559f1329c59e282a2022e26ad7213ab8228ee3f46413d24b773ad67817758f3066f585eea1df4935270ab1c876f1dbb515fa458b70883cb886d7b390272b221a503a231dd75394557135d300e5f6b53992dd084c9769e863c1b11070029b6d5ba508cade4568b2f9308718b1b47854dfb41938cf43139e1dc29c5237c5e01d9041618feb56f8ab6af72d5d8f163f47c597c1c6efa0b92a78e1724848a7bd3640f0319f4860accef846df3c4382d771e39446c8bc950de470c48c99bee39094ecb3a8ccbd8077dec1e051b80b5019bff1f264b7a3d1b6be2a947ef38a37ce76db7375e4c20299381f8464e9a176c35d8e11a158f274518634552794f44704f8bec0cfe45d122c4e20258cd25c767d346486badcc5e2bf7837c0bda97a2465578f07de44830478577926b85fba768e5cea43b8565db43a57ff24461180e5daa5c9f6f1d11912893eb670bdd2c1558d35bab50d640ea20b6ad366371164a4273e5877ebb6e366996940e025e9b3b542cf26b143fd1895c01cd29402e9338600d8826427f450f56a568957855e52e13a78ff25cf3f9a41b4d8d4915d4e0b451ca4f453a519be758a54aee56c584de54905d2a2b77891f311e638c22eed7063549881402555af9578ba200441dc70b567826bd2b0095e471beef0140edd6b62f26d7ba449f5eb17b92d6a1739c430c15a343f80db6cba35f3770d106713b6640e56f9d3f42021110626cda0d18b99c2e71e64960cd8daf0882280675c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71a23b294b2d2231009e495c208175821a1d66752cfd05150a3167135783f66201777db67b04d6a604a59a76088e461e50cf19e8311f20763999842343dd0ef100a88bdc5bf850161b29fc2a2eb1546b5c9900f924e358ac5f3a678c287ce63a5d37268774d2511c68ac31341fcc5d4a2f328409087375fd37ffff974dfcaa641b29962e7b67c7367ce6deb243ee8c113453bfd64cc65b6e39eadf114494376b319e1a86436b911574bf40cf352dad571d10cb1d5069beb778377a5d169c98e27131d05a2aaf0db200ae910c56bb32d3065517024838baf56d8b6b00083b863400c130eb47d505fe1321b3c6742bc896363c4fce16c421685cce77044f150c4e7caefdc06e4be50b1c487175581393d556f0125b0a9387173bee022814f06c4870c4589e69343caf15cb203b1ee5ab3f3521b549530573545a46ead248c9db6e55c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f718fe3513737f289298067a04746d31f53ea250623e928867c1714054ca061721662f0ca7d3f33890f24b3882418867629e1125e73e371183cd9eb932deaed6a398f5008391285b56db5742a3459fac423fddc085c36de614e5395945f4fb501145101493e04c2322e7c82f11d17d48458fab4f437aa56c968c9be1327639f16016dd53500d1f54713ed5f69305401585f611e9f327a3ca16a1410d7562e169548e96a254c838122354853af4a37512c23ba7f613685c0a70f407ec17e34d5bf3c00c65506926f6d1bb0e4492073f57c3b865b802f16c62c69bd0126059a6b6b2a059d4f3ecca80f0a8762aa5093bf85795ea52263d2fe031db17f053f82f62d7262a0f00912b8a75732cc241b9c82b167fb2b2531111f897d001783078b20e77d2d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71e9d94a77b0f44d5922c1c15048cdb643355eaf13eaea07491e0b8447aaac9677f127e40b9c7ad17593da1d508c38711b56645a64d9f57555073dfa217a5d28068fe1831fb91323784e233f68e7e98772f2ca4f220be4156227c250569dfe1268d04b4d56a0482f2ca35a312346a4403297935a69c77a0475e9bdf16de8da6b307c6cf93d605ea51014096360ed9c542c2a9f7407c3a1ce5b8b4afb1dc4807a28bfd85d0c00f46261523faf027fbbbf76a718687a4e39db6ed20023374d59ae57ffaba86d9ff7457c49508f5b72b5122343a7ab44426e4e055eb5912a52747a36a033a070ea8710313e111a1267fa8330cf9789250096745169f0f94d3843be190959165fbff5e134c94a087840a25d774d8f302b51ec1a1a0938314c47ff5e092d62cc76da52cd2c317f7b46c24d1f759a10a1103961603d628a9d6099975938c4c14b2657240f23c70fba1b5114fe5eac9a7d5fbbcb9b0f9dbc7d1468a19f71e080136a3cd3d865326fba51dd9dc919e2fe25778560d61c4dbbb809139fe416553e54494f178174c063835acfa5e45dcd8efb69d3d19107bd282c176b5e8b511c637179c8e61a3af4d7787081fe04701db7ee33f094fe0b7671382510051737225a465274e4e05df69e981a3a58942a12dff93d14d0ee6032acfa6105369a7de5857465c929af6909fa322f66410924dfba3b1f8a9e64007aed83327e3dfc6a1b69cc54c6ebae354911fa4f4d97e80194677a7bdfb9c8362f467515efd8b8251f84be1406ceb3595215057612ba396032fa325ece94af3eb25e11393a43cd204a28ec035167113b2d8efa28094c427d7737672288c87618e07aee1f68f30d34aa71f767543c5a51e09d864695cd910d28a172559491317bbb3efe3cb1f901043ea1673cf7df0511fdf6353f360a7926ee6c2b088d1e8c402293bc4d7586777234783a0f45525c24534b8c6955e96e4b8075a41285fef33498ded516d8fbc81d52421a2306649c3f5a273754651c1f5c1554be759c758858268e362bf9c53a4356aaf4579359cb001da42e139078721ce93a7d029b61d92f8812b269a1e2be73c96c912974d2681b2be0dc7916fd293f09b0f3446c363601e302540362b39e619579ec553a42544cb5c49b6898233257d5f9b71938c3ae03a6ac5b442e94194004f03d6f6bb0b30ea05aa612c4065555d345c54894f8c72949a87f7d344540131a90d53b6527c4203fbb322cf9b73b6df00f6534760bad113a7d675d5131b96491eabc04f601084e9e15c25973c87615de72013a14a5ef063e8aa8332c2a2908f3bd87237eba53371350db6d6788d9245101266ca51e9c160d5c196baa22a415c52def45bdffff2a184eb05eb5b083119435363b81773b5736d22c6deb52d85c1a43e037510dd0579c5b644f8dea5a27a789ea32dcf3e65803aa4d698e0f0f1e578f59227bf6a158fc73ad1e78abf24bbf224e030072f4503397e57ceb14ff41ccba1d6fddfc1b6c53d8d6420f029973cbe9984c549208095ee4191b044dd6490abefb392f1d305ec007d40b8dcbae356e9d1a14ba7ae5413950d72f6b8eef4644adad37ce039b40ff32463c63d85e36801acf337e85556743ebce090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac609946915c45590dc0d33399be9b1982510769605fa808cfca74751d908a7845883f18213bbb5579ac22194cd6364221d8b96cd6df9f568c41d8496430d738b2846c35b7a7ee629447bb719bfb3070851eb778c8679c2812d90f7b05eb46531353b3598c54d1676ead49528fc14a72a9e50054e36d0964cbcf574624440316df070b21762d952708f5ea4bb3108b4c356e9223282135156c592312c56db6715ae0383a862b2170d38ddf2e80604259103c433a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae9d8650303ad2533d25b5e2efd8a3dbc4d59019121fd5d69993f28ab6a7729c7e792017258a253874d9968ad47676852665e1255b70a01d460e012994411294533fb1e3cf72306c076821734090223bebe717da8c37a75cef01d7aca63336bc936fc4a2c5d6c069e3fb14c7573606e8a48973a093636070661fc3ada79591d45b3f8549709fb338e5c06167d346939ae34c17b7b9d7a4739cda34ec3ed4965d62c541244fcb51501d06c5eb3c7d04eeaacbd27000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012dc2123f25a535ac147fa240c1ad73899d9e64fdb013f5e7430683a74280208374656526ab5567e0f289221bf43c16f59667572f3409774598664156518732072cbaf3583b7f21eb46e2259d9e58e1c354ae632d4ca207da387c93720a67159383ccb6168c08c43723ea85c1a8ecc52c7a97f531209e443f3d4fc6faecba50403ff2b4fa01aa50584775b0732cf8d51a054ba63e968c0439ec5572a7fc7954a9158b36d8c10ef5d80d54769f84c160e787c4c44000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066f00965ca0e4766e7b28d516d8b001c98ae5c407f5ae5333d42176084de076eaea8705f0a5cd437f610527a6a4b2214fa007b4aa3132d0fe5816679868cec253852bc54b596271b78743f39614c663e7731a53afd45a2536e0fd31ed0a7c91d4472b43e39dd8b55d21e796cf9a6e76270b7fa430f60851fe538617872c7ef18211e511fbf5bcc194933062483d60c143f08514a98dbcb1583a9933c0b5cb242f08768062be28e2748686f759e0119781069e72a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049077152e9450160b6ce49600df6b7378a527229c94b1e3e62c432386cb0353e6f97723179def328af0bd20b7053025236df6c57d5400e5f9a16673eab5a1c4d3d0744618f924b65ddde92709c86b856de0047704ce2d17b579e2421e6946c041c18612555a7c92452af77540c717769f0236151908fe10b0fc2225c54c2f0488e65c711f49ebc6ed55a412a5b70206faa5c0e5c9490af1c6d039b70031493369a132c75f2d6a9317277181dafc8bb2d68665216000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050d63c129175c76f068c8d293f2c0e7c8a435f1891283308fddc6448b0447b3a47c88c350ba3b017c47ec34d44b2ac5c226dad2100d3f05334388174841aa22008ec6f6738a8d310227c7971f872254605f86771a5bfb011f1d28e3393816d2e72d56347ac205004b16ad840dcb11b5129171b6d7a89a059962ac276d0dbe76d48451234befd633514559a10a4daff0ff8f0b829ea98ef231f3998773011e77e42a4f0590b63683e83543c5faa2b7e69b7c0814a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d060e159ecde284367fb090c11260e6a521070050bf89c33afe23d2494fa504bcb14820120243512a7320c3e9ea5097371a7ed0f319acd21ac388e0040f5ed59c987855d5c6f7d10d0def93dbce18656822e12310e4d983d63716d4a132c05174b3f344232bb8f1f4ec55071d28d853c0881622e17686e6a37000d18e05b8d0ae357b0435dbaf46d61dca85b6232ed75f766c17dc24eec0258a919333ee86438397efc07e45cd8675c49aa49a4e1540a4b6669080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f150072d4ecb361c4a64e03884418f36ba903312a03ff963eeaa542a90171069da9f2d09efe20c3ebd32ca10dafdd41c14adf260f33d1318279eb231f2f532235dafd04499043f4a4cdc801a5e8b715672d98a210375125718cd2403339d432f1cd1d9415371b3141d41a60d1b9a241be7c4351a008c627aff46de6be4e639285d1f6a188e3bf541efc3a75bafa90f5da732c5630ce0053aa9658b61a192c51aa6ba0b282c4a5d4be6955475cf00114c5141894200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000463c068740f10689eb3460f5ce5db3642ce6a5ab322947ac9dc4573ac7a3b31708ff40ea6256a49ad2e2b20aa8c0a3f80229f77b37de53877cf9f1d69322d68d3fe4f71a17ea67d337a0e4fbff88e481babe0202092c55be057f350e401584c79b08c0154304a7e348dc607e866f560955fcd0d0b15bd62ce233373233c3846fada7e3327f5391f4a8d675adcffa50a28c93c3f2d815a78f43df65098f5d439ed97fa1fa3e7cf1594da2e0da392970d221b8556000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067f2d91b0ac9c831c2115a63eec0dd65b683291d1eb9796ab7c5a80631618631d023eb4a30b94349d0454130f5c88401e9f76d38545fae74d712b137ec92d55dc5335c796a38bd6f243188570101447104ca86746874e653bf66951eece6052e80a6e569ecdf2509fae6565c8b818d558bf06711fb113372ac25e44febcc3b128288f200b9e45c138ee2637eaabe5514ab9f4f2599fb170a4f9f7a677a84bd43dec0e56569f43c6f7e1fe6343a9bde1d924d25450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b619613fae3cfc508af7f9667cce7151c63cc4319424042196338f7d4171977eb42e78362c79231ecb0dd97674f41a18ee1a4e57844f2625cc608b776ca0de30927caa316b3e671ef0b3937ea754066cc5de9c6d5f1df20014a63614ff808078f13d3a22df4db606f929421b1c15d35447bf490019c11203c18c4b1fa5d18272af4a56607dc83b23f1591713b57bd460971a634038003264110c8777409fc42f0525496df446c853571c3e1e03661e23b868e91f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0c227146dc7d7753404132307ff5057f7656a4571a8fe6b441f2d7a58af4008e457cf03a50d5500fd69ff051371874743e3cd70df158c5b5b1f4575727dbf2eee99ba0fe16e515d39c39d715eaa4811034a3c0d7edacf3593e6017bbed95b3fb9f07b7d8bbce5492878b25cf129566df2334674d9e7344763363018eda75436be4ec22352bb3f66e4c5a43850a1ee3148cc7f2fe4fb526bfba15e277053082606c8991bab03f926bbe5b81a8fe2d16e14c6b76c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ca6bd7cb30d46345be0c235918fde2ae8837d4d51947242190f83189c64d71b8678ea043a5264022d8cdd7684ef5909d9a81a559a7321754f70c63e44f91d56d0054576dbfda93632834c1282979e243c38b93b52f8962f8116ac46a1131739a05d42773f7bae5897fa7b344ebf5f5c57d9cf4d3beaaa4447649a05f795061079e1d84357245f3d3c7f8871e170706ac9d7ab3783153e782efc3e5bd861d53f3398884d0b69686f527851646bba121750364c370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6a4b008ef47036ab60add25ace05307d9e7ea3cbe7bb7693611394b12e8f957d024f105a63f071b33f4f06557c558093021d1726b232129359a48604f5aaa19a9606642e174923d55086210103ed22a723fed07dd436d18bd109c767acada0595bc72523a19000798532f707bdd84110b162c1a95197d71b26e0c30395ba84f4843da769f985e0615c5db7963f20a3c9505cd3ca836a53bae33ed7d62219c50aaa9d516f5a6fb236ed4755f8ac7bd276971860b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ab9b8689d9efb40acc95a577c89ba40d9d4aa6ca4196c3b61d35e2aa750585935a3b62b57090a7254039d0e7a5503753cf83626e77a8971cb0a19246cd5c01cae485440d852ed6741a1e93236bd3870d963f0482b976112bc431d0b26c95c70d0e3905689f81e16acc0186ebf74e6387a978b728f00af58fe23e75db9108145b1321655eb2f85188b671d066b41e2785c211d00cf491e0248fcc26f96fb9a558a447c6e6a806827eb978076daaad3748282784f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007baf632d72ebee2ce5dcfe269b8b6849bad77e7e42b91865bcc5882561776d4e786001472944d4504be7c94abfe7c8078ddb7103128f3611698cc1033b4e260ef9308059b5bf2b36dcdaa92d3d77442f21c8fd20368a6a15dfb82324887f8b60d6f2556c85e816744fb4d40412db7740ca88fc2a2d52de290e1829514b702a4c1e9e98416c4802242101e8070e527f4a1780d11f8394ca1151f65b229c780618b2d86c7c73f3cb20c4e770087be5b51b0a2d4a0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050d63c129175c76f068c8d293f2c0e7c8a435f1891283308fddc6448b0447b3a47c88c350ba3b017c47ec34d44b2ac5c226dad2100d3f05334388174841aa22008ec6f6738a8d310227c7971f872254605f86771a5bfb011f1d28e3393816d2e72d56347ac205004b16ad840dcb11b5129171b6d7a89a059962ac276d0dbe76d48451234befd633514559a10a4daff0ff8f0b829ea98ef231f3998773011e77e42a4f0590b63683e83543c5faa2b7e69b7c0814a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084c37e486286f440ce660670f3ef4904e1e13f7b52578b7e541c082c78cd6269bdbdd2794f7c1d6a9fb202550557ae2275a77311f1334518ca603b4f8ff782253d8751150df639227a93b47d95db752d61c5501458d2966e821f4533446097695bc45f5910efbe25042bad1310f9de694d06632399bad93f0ef2ee542c55b92c9e75b53b286740747947ac74933fa37bbb485744e4467b03bf788e17601881721e7a0e4df6b49819d5e0c6238c247f754c40eb5c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000666bbf1a1d7cf20962c32a7394b51023361622698fab620c0da3c213bd3b6c561a9e2747cd132e672fb2f11bd5a142319e541255985bad3b0f6c4b038ac967301005b96ba4a9224fbf630f28d91d0a6c8a697117d757812113d49e43c955b6397cc19668141ce57242d11f18fd5664117b288a1923fe532f0f90d5605d4b7371a77cba2200ac31563c59644736d73a20b6037c78282a2816c89fbd3b42dfd54d78e7841be67eed7295468b599b410b7cb0a8786c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a2dad25c306b5174bfe717b2c50a0336b95065c8d6b0210a177a150c9aa417335a07839b1779744fdd671673dccca4ee2f64f5dd9b1307753deaf40e1e6c241940b8f3555dcdf02a3060814569c6a56cee48a352e6d155c56e9113fde8d4a3acfc97909900d3002dab91b026cb53b0be4ed196e4a7e0a62a03a8f5b9aeef200946bd935b8553753dc9b795b7a1dff3f0b32bc2a5a4c6f5304be5a63eaabb44fd109fa55f1bc53689c0bda053153bf1d65178f020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000951afc2add113b1d4940630cefe76247f2d79f287384425c3e9b0e3db119b16fff0b013e24009c42d5c7c44a5b8a88212cb7842d4818dd7cd4dfd06ea0cdee269dae40399737fd237eadab6e663b38463c8fcb2d23c644785e2b757559760f305f4ed23a34c0a0122c77f602c731e871ab678c5ceb988546c5fc2a34b506c163ac25bd1df9336b334e9fff32f0413b2975edb0252b438b243f94141e754ad07b31bd573ee79a80315377e00e0d3c2e5486438e0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d198591b5b43615a6b38353f8a4aad5fb48c555f00e79b1100b7671cc9b83825c836be0e801d2b6d5e7e2a2ef6c9657038f9812cb220026e946dc5562e0bba0c29e35727625306081ead1444e789705e95f98b20b46b5336fc5cb518dbfde5340cd88e35fb0e2e1e71f7484d9d8488064f8be31e126874583013ef65411a3909154afa4a72e0c40d07eef500ffa7c17c96e6cd2c301c822d0197dc1e3822c7221f6442728d2dde2d3430a84754a647687f29654300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f1e22129047e670ac3cba24365c0a21cb71e567bb0cff44aa44cd5bbd57730f1e83a835e0062d1cd8e92a1538de8b6afe609406b99e8f2f2fb9ac708f1cfb2113e4630d966daf24b207344a95041103aef257340d6b3848b61fef1d189b674058c7370bc296614e6611a45a9e38174e70cdcf004f001d70db75fb48f476c10924ef2207f298d35bd2d70a100c4de6029aa2ac3e3d5837195b4f960e84daa276bd190e12142df902cea9ee3d64e8134a22aab52c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e95a7290931220d6abc3600e03e790d088a526fa1f04c4ccf9ef5301c3773482871830e8b3f3201d84c6f293ac6730278624d3e685a0c21384c36122e831e7e47e7ee1844f9510ba9d1136a11838538fcecb964e1bbd0173eab1312fae031123ddbb3484a55ec77f2ebb23fe1d67c7300a2440331eb2342c8882638ae5b5d05c933aa63e100e270740ac44d5e1eea20777f1744840f8256cbbd64174aa03d5a3df8fb57f2f4cf13d868711c912138303efcdd36000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000068dac87d9c3f0d25b8f97522d646ac22310bd237b245981789a999306f60a6231f175f595dfd3002ae47e62876019e47a6ffd152a50d5a09162cc16db8ee931a326e774441c09b63a22824242ae220553ca9643d9237215c68c8ac005abbee2d5fc908563547506e37fe003c4effeb34962ef43b20b58748c8535b0199dc4a058796661815997d6be0f7052c108557193fc0c067f2842525135e531fe55b6806467db709054e5c2d4dd6981ac8f2636bfbb84e250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b9ea576572128401b8dfe43633f24c5acf62d0587a53a22099542d273693812fe65c6808290d626bd202ae699d58c443e90f2823d2067b0c81492a4c0d3d353b82cc9a69f695824db3b03d713e18b20d1ad153010a723757ec65a518b18302723a31e5086fc4535b0d69332d02af861200f62b65b7fbc96ef03bc82da1138b136d398c3c89047d3ae4f5b13c84d06f613545e41b72f8931920b7c4382ba57975f9b1fb460c099d0f1a108d3fcf53930096931925000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086fd7f738fef662acab84046c91f3b6bddff06359c21877331b45401df093d31ac155774ca4fb2269c97eb2af6a5046877dd3300c1e71055e1806476173bbb19e7fc654c3f445079346526770a0d291c7c1b896e90af5a2c602eeb7d2e08366ab25a8c1d9300870fa4a08d0523ddbf01ad9af058c3fe821051bc1d5bff1fe276ca01ef0dad98302692f45b4219d4015c632b1f467b3c2837896a5c5b262cda16bc782b7500e15b1a0e42b63db726de2b709bc60a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf9fd09c255eb50668dbb1fdfa0287d0786c042602bec282e71d60c99b93f3617deff1cbf085c4b600d91235f969436b9fd7f6026aff728669e2864886c6928c926a339bbea764ac0e6ab1f3ed5971d8e309657971a6064300e1970bae11b3ed784e538c57bc83719fbaa3d3fbfd24333141110216d87682264c83fa41896176be02725b2f0c152ef90e453f70c885a96870011295c4b3549b4b40d996d642ba05d5f5152effc2e0e01aa08fa4a6446f692a43d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000344c593ee5df6f6ddf19570e8efc1a70b4c7bf3f4a69f936fc132d703ae2191a2207f4421190bc242864e23804a5293d26e3300489ffa11da357d91671b4cd6db159e024d946da15c027f810e3fe4b453772077698ddfc1a6fe5421cd84f40626d66684402e97f205384c3234e866514a521bd611622c6624fc22613da3a21297bd1826a15c064394581b24cb629972c564d2b488bc156052feb1400b2e7b139a4130c2e3bbdab538dafba342f84850bb743f07b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f940a4b1c142e494761c01cac71b014e71f656187ad8b6b457963793327ba6637f52950a330641292aef16c78635b4640b3c8517390aa10b5f9be2b132a8a20ad2f443c40a8c61de989a37cf9ecd76fb9359d7d78a08323de0c2f1f1b274b546372a44e7b4af036be35bf64b6a56f642055f846c9005f3ad08a89162bb3c05acaeefb2e8198365cd53ba82a4470033bfe3d3148e54c6f1c9c3c7b4fa200e02efa4a3f001498d239ccbbb600d4e20b3ad71819070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000233e5f249efb8f66778b590d7109ad025ffb14414294ab650e064a6ea824d45a3aa6830570dc794ade768e5533e2f5771ec37d149665bd2dd628da28bba8a93d649f5527f2671a4fc4afc91d1e1841063b38e04cda100e126e9cb56426357931e654fa56656b840aeaf49a716c5dd4089c3a6835903e9019101e8b3544eacb775e8f9338c732fe0edb83737c207dca1a1f33de700da11d735396a7451eb265478debf047b651f71e4dc88f0f11cd0b74d334c34e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fc6795563e2ddb18d326dc1f8366023e66ae5073bb09f96a5afce418e11e536efc1eb85257535375108e5010c2d5e03a551e8b121315911b5e9d221065a08f17c4f49416c7ea7433a29b4d4b78750a0a2ef8043594c8d557702db96f2627234265774350cc969a64b3956e0133a26020e2d1492993df80702268360caa08166eb1e79a78e580ab57ba22516d07e13940fbbc571a01f8d63ecdd1f2273acfaa5e5da5221ac5c5390a450fac6a3f1b2a53809b4b0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002f78564c461a8a3006cb8d570b1f7f39717d74362492eb776317c2554de1372c42e7df56536e861562a9cd094d5aaf391556cc0ae0a5aa7e16a5776d4e9d2c0d7054e3715841a343ac865a6019f9823535532050e858934ad7cc451c9cfc5244fe802960170c0b2f6d935368e03ac9558b1ee75867f3df78e7402f60d71732198136d15ab378a643da0c9d29663ffc1000067920960171163a38dd76e36bb35a97645d2653657b3b8f3beb34efcf2a769880a86c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6d52343722a5716390ecb42d8b9054cf075a63fbf7f52634d0c6e7e9222566f87b7b403e3b24972c424c504f31fb341205a96733a693761ee0d8c312e7184010800497adbf5ec4b27694c69c435c770d5bde060e6bd44564f93ee473e68903b8f93c14b2df3b122aa3e356e3c8a312239f80e17f77c09167acb8c49398e16088824204b2dcc7b7dd3ac8031b3855733a0d71d19595b7e5a0663990a5d20b87d6489142580944006fc5e7f4dd6fc0c773e1e7d1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f8dc24b45ea3127890316048c72e8685587a844e3d01e08de30e55d5e67cb107f12c127eb0bf0417f65bb3f1a3db94f320d514eb2ea8a1be6ccd47620aded437d63e450c8f9af4b595f2c47c82e8b70451a781a26a5385530120567e673d55d5d3e375ec3c28e081db8940e4f0950408850e4174dcf7466f81cd94c33e04f2b333c233e12b33f71745698739c6ee2445c1c5369251a0b6ed47a2b3324f715701ce90109c6aaf2524d1e2c639b77c94a4bc9fb70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057182f7a11dffd4df5b5e9586ad49f612f31215b7e6b6e7c677ce871cc518b4e6c485735c829bc7e73c68920aee1a61d21aba662629fca7439c2cb4357b38e215f8f880191e6bf7ece23c7129cd9764c78c0c237859b236ecbe2c779c3242173845623154d6fe11794e970286e94820c7d223f1c4d5b0f6bc5030b571aebaf43d5f3307b07bde960466455126b628d551bc804682f36f6233123c510223f8738683e582f4aca724ea211a50f875100716915a1380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa5f8c22e1769479a4a29221d46c9b16aae9a31cf51370169ff0fc63ad77be374aea2828e93a546248605e37e769216661008b05e91eab4e6769262484b7e14c38413f06a5990d394337fd5d5f9fde6a08db826a306a263067fd73607669dc6b28bf741c21efbe6c949d110f67946470b3d9a60373c6ca32a5d78240beda49361ce06a29b20fa02cfe166c345e8ced0b815d8130b689085de11c0d194cb7d1253b89c84a1685db034aa4955e2023eb05d09df941000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062749f6a520c611c525e873f59876c516b38c7437ba970456e5e685d3065a315bdec263df1e5466fc243e41439b88c26bf1ad005e0c47776176ff5250a89dd0d29908645dbb15153b8bd0c0d069f1720eb00d6217e16752f8c8d874a015f151fb8474a5bb6a954118b099d128ffe6364ad31122c7e10387684300b0aff908a144f0a4c251377d672c0237a39e46dde74ec37695a40b7f0710d5836551bde174cf8354a1bf647062cd1a97b4747b21e3cbf90fc790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c38e070ff3c63b71cecc453dab584330ba1f8d347ee1fa248ba11507ec365d2804c2c86f7a92f5069729d92c26ac117d6add8f552d289f03f4000653bac40124e4943a7b9c72aa7bb3df5e145f5c4451412088190d24a17b3896b42c34a1ee13ec13494c5ec8f564feb22a09a5d08359f6866148c1cd2c1f2c41ee047e897e1c27ff631b97026f5d8ecce4080270840c147ba3212bdd8b0c32c81e4cedcae3405b583f635f61e4610da2eb02424bf777d5c1d4642009e20ab3c5b50aac51c74610f60205d17ba770c25d0457c878096e341697730f4d4c7d1b5cdb5f5f4ef1152fedbc0b5959cd3419c0620c82fb7b741f24412b41e2ea57679d5b54835fff7112999d367331350a5b0e714a3ed9f4461715fc034e5b5961b855b07bc43d5d2df467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1ace11e8655e868a5fec221d3b67655e43b51d1323c8866e4ff22cbc0595b1a553331e7a60435c3c69ad974d3bd366c54cb6d39512e78f0b37d829400931884751493f926bd71aaf3af6cd4b4cd6d9a324a3eb6741f5a13720e71bfb4d501fdb7d2428b8724c339816e16f443503f515598decb460dea0712c49489510cb81c464fdd13a46026df16b6e0e71530facfa2c24035b4f69c7b72e5a5365675d410b4ca4dff7079980433aed4e4f4a074192083975cb18c79a8a258f878414ac68ad6ccfba0177ed183e07694c745573fdc4156662a1679b003a4c462c4f4155f4fe606546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06125ec63615a38277e0b4200f3842b176aa1e1e2db1b5df487955b55386c5af60ec8c7776e2b0681b54906f717f6b837b53e1b071a87d5378e8aa0072646f50420bfbf45423cca640b6e1253d9e63fc2509ae2b35c61bf2461306193cc5915d68382a92273e90204fb02cd022411f187eba689b7953e67e4e1b6242208aac05653779f004e4a530455122e84cc8ccfc5ec8356f6a18d61f54ebe2a14ee48dca3bc696c611fd99c2768afb033ffb03922703175e0698694116e0531411f83b7b13ef1122739e7d381de221523aeef7c33f7d49da76a0a20a412155e36a09349175153fa725fcc6fb5ec87d334b7811960be2e61b50c6a4403404d03f0ff8e66904a496fb66b9604a0a73c863619930f116be73497136eede1854a72b48ee3dbc18d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06af5ff440abba4d1517ba0e148c72f701fabb9a387cb2f619ab1c094d6a2ac907b8d694565fa2264c8d2cac1661d2f639472d3a4a31ea2f53d8bd6f3080a4e425e4ecb6257343e32df8d5b90eb3bfb14ea4af6314318f4241ffc8d02fca957c3e31c859786b029355997c32230bb57922fd448719f63c0f18c7b666021408a81a27ff631b97026f5d8ecce4080270840c147ba3212bdd8b0c32c81e4cedcae3405b583f635f61e4610da2eb02424bf777d5c1d4642009e20ab3c5b50aac51c74610f60205d17ba770c25d0457c878096e341697730f4d4c7d1b5cdb5f5f4ef1152fedbc0b5959cd3419c0620c82fb7b741f24412b41e2ea57679d5b54835fff7112999d367331350a5b0e714a3ed9f4461715fc034e5b5961b855b07bc43d5d2df467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1aceb8975f7da07e2da1bb780e7021d269786b235ce60cc26052099d59ef6349046f303159241f4549c0627854d60fa550f4e28479bbc94c4f3ae2ed3ae850ce0b07fc7950b16d5746e1495a6430e2832e9f110d7daed665293e2b5a1884219e700dcef6613845f25c5e25383545f72a53d422e05c46b0456b6cf11d4b2cbfb37a3bcdaf4a33bda42079f80f49f03a0a41030bd5285dae9943f60a8e2561f39045c9daa5269223736c18c9305e60212a093e280028531d5045a19d6b6d16baf638cfba0177ed183e07694c745573fdc4156662a1679b003a4c462c4f4155f4fe606546bb1c6de6cd5ce05fef215e5b7b588807487a06e00b5424e63e4fcadc944ff04e2b0edb64e714c2a3d967ae5be64401aa863130682b219a38700e7bfbf726078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc061374f44ece09a900a4db1203ff16ca53e9cd952fc56753112b2d7201528958203a9bae22866c4233e9aa376335c65036d8bc7e512e33e01e852f8a3effa1fc31bca8e71e83b28831d1414357b37a2531237ef763da12b22e43dfb1552d6bc02c6e3ab34f02ef3a06eca1517000e77f56bd5a4b22610e7d4181fd3170a19a955b393c9e52cce1f82fdb4d392f12d9356357f6642853360f1888bfe42fbebd123f6e395a027b01d045e3eb3131b654000993840944d95adf218e687d43f9dca60e22b3db30cd1575481cc8e62a00c6b640ddc5d41c29820d4320721f1b2542db1be0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1aa501a601a75a53386dc67c43ddb18758ac6a37140e1bd67746f306474ba3cb4b3d87660dd44e0c4e0056e94c34274708ac04a033477e532400f2aa7822913e2448e98d4b5687496345f8e3740e01d74f5b766a15b7e8d202655fcb15c04a6262b969cb17d668f776d79dac0791e379326d96e1370efa150db5b4a87063ba55482e864e169b5d2a139d72072e3ad14c50ba71000e38a871243cc7a661ae2fea3d5da95212a758e62f0a479335a1b589493825fe63fbaaaf006650e62e29318441971d3b66b62a36051f55f018a71aa411db8d1e1f587fbb791cb2b50bc105d005a3847e1e2efe34519dbee86221de8b6fdaa7b107104eaf32e43d3c2c7b79e366a9d4ff15cef1901448cd315c1f03be4a6c7fd00d996fc4147581ac0f4952cd64a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06650c660b439eab4f14fb772b2ae7664fa211300798b4363f51a1ae699ca71f39d4ba7f2360b0c40cc32686411b9a4256b5b1367190c3e867f8d82d4ea717865e7284e271c9dbbe45e21b3d2764c5e96dac31495e25bc9141cd9ae7738d37e11ed4c34c309eb98c5f0d5b7d1b41782b4277c458186bc5c405cd739c55bb15a86f2e14883e454c07547cf6a110ad57757ccc50f64813451804219d7540063119611e3c83352fe44742de06777a5a240c7c3100fb0e9b901e5783230f6cf86e2966ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a302e156a12d25523f618643285214837d93d870438f41255d2034f420dd5436dbb4e3d55643078586c5f626c264f1c071e0af17cdf7e5364a5e9884087b1f673dbda7967cfcd393e2842154ebe84e31c57ee8811c72ebd7c227b58064af2263c086db852c53965669013594bb31cb475539e2e6879b5b9052525cb4986310d53e774df29a26a8343d3ec04563cd4bb2f3c145740c05623658f9d946eb900810f1044ba0c344baa463c289c2d52997a6e16dd2530153b284a71204c546b4c3e094b01d13a2e05f641ef30b83f2d3f9841f868ed5b84dbe825c61512743f49482d2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06b8086344c5e7e91d8039e415c2d91c24b9ba7d6fda48f0467e6f22688587d10eb75e32321ba6d3708cab7e50064e4c72f931eb46451d720310efcc1ed09bd049b43ac7125776785fe67dc243f485ea3ca4f56b45cada4c79d04e526cb73569199c59a004d1970079ebe75f56976cac720e63342a72f16968701dc702eb3c68709c803412a83dd157aab25b35dc26c35cec965b16a53940737745c3729d23c8436b40115c2c2f0442b971ad527918e576e53ad332439a436261ee355dba20fa493dc1643eda0e546870bee7146194bd1a7f06aa5dc2ca6d52a2df0e6579ee671b9a86747c5602070020a605629beee921b24ee52947e9245a81a03f0f4522b05ee1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a980519767505ca33565af47c0764b52f369e5521aebe76741558627bf87f7635f378e424fd306c2b59e4343b7ed6a51b3e1604684a8f5f0fc8e2016909d44c46009c26003c19e3139cbcad20e2ccef1230b0611cb9513b161b89bb580835cf6aa209350768154a55ea9767679cc2560fb98372446d7e834da60bb64aa692117ada9a937e6571134a975a2345e704345d140c0f502353c80c7101795468ae4e2163486663a8f7c03c7fe4123c23bfee614fc5a21e8bb4c76b2103aa79b6df064e4b01d13a2e05f641ef30b83f2d3f9841f868ed5b84dbe825c61512743f49482d2aba9c55554ec358d44f6873bc31a34e31eb5e7d86ee611f34a6dc679601e86b2e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06ad17b62674657c6b7479e855818db4310ef97b155f463c22b502b4060867bc3d22f5436b4593fe0d74bb7505cc6831390149e76702ea8f0e277a016b06cf98188730f86935508175e191cb57d915162ba1d12b44ecff96568a9e0f40db6eef56d71c335e6f73ec504ecb38719454802710785c79794e4d1ac58cb6241bfb656a6cb33643419b813626cf99479421725a28ae504f3a2a8f4806430a42e7f17f53a31dd717f5bc777039221c0df1b91f18e30d6f1c228d16033e86b2246e4e845d3dc1643eda0e546870bee7146194bd1a7f06aa5dc2ca6d52a2df0e6579ee671b9a86747c5602070020a605629beee921b24ee52947e9245a81a03f0f4522b05ee1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1ac5ca813567b48e5878cd9a2de41cc82fda8cba4e1ac7936b72ffae14fa70754e513cef1fcfbb934f9a8c2304d407705f1f4f197716f00c404b0ab35cf02e8a2d69b34a3cb9eb292016408602d3082f728b2d0f607cb8b06cbb876c2f432092009dc6e4417478b4615c9d5e7eddd6067591475150803c1422bb8d092ee5eaaa5f70dc8d0512487e088319ba69ba40bb30db842f278834981c4fdb8b6d6846216b2fcd63619549613aa0576214d8767e2acc902461a7f6f472e4de865ae5ef3440e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06c5479b1dc8d4113b240b57109aefb94c1d8f526462b85f69653359194c2a812d96c2382e5a701616e4d974216f6b02388540fd1a63062b0564f2c843dbcf1d2a05df7c29d7ee2129ed04710eab0a466e729c21627b8b1f6a96118f550647e95765223966dfd85e7545bfa362a466c5661a280b79833a886a53220967e7e4260cc2ba6918edcd0a10442aee3ad9ff085f335d73624fbc02695cc8da3d82598a29c6e3e017cba5a623bfaeb3630360e243f2fa9c383bda4e44a1a4cc21fee4b5152a34ab0e54c3fc162499c0518f5e2a726c34eb2342c287687b4d3d70bedc6b79e6e2cd29ec4c6328ce94dd27fd772d763fe55c202a723c2b11b22a1f518c367844f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06594a812858d1ed0593f447563eca3651f46ac776af9dc20a8835117146bfc51f454c9c3811a08f511c395d43eb19dc3f8fe4e6385871f01cf43d5641d3ddba21f2966504261f30481ceb6129a5d60d25771b192d015ddc27dfbb586be474053a1cefd449648f9157cc87431337a5d91c238d1972731b790becfe1e70f2a6ae6ebfdb98773e5a0873fd18b71d646a2e4fa402e4202c97d023a807c0543fc499158bda947d2571d51d723fd161269c6c07ccc88e0f4f0cf7113330703abcf0865b63b38a061504484bbc508302eb15c90a95b9767ec1b8ad7a8e19c01f04e1be311f354f584f219a3f7f428e16cd73bd78760ae509653bd45221e722548ea17a7c12999d367331350a5b0e714a3ed9f4461715fc034e5b5961b855b07bc43d5d2df467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a8be95711941d65290d28a9722a4e581ff172393df8c7a1400acf2b41eb2a2f096b17b50d156a75703106ed214225a4144a777d7003b39e61196f864313f5684649b7fd621a5fef2f1d1cc30b10fba8515f243b212cdfd73b059baa6ae364e270d8e1c80281bd92605d4bbf2fd33cad5181589958bae6592b8d89f5370af033442a750266d967e551f5438172465f8b43e10b9f5a26803a4df45b482ef8ec8b448ffd0e0ee2011a38ad206b586ed20539c500f302694d71447f46791a597e0b7682edcb41591a460cdc9d2d7d9da7dd3aa3f95e1a44f5605023ed092c3eda1e2adf641b00e5a0f27a9465e2337263ad6ea08dcd7b59d03a2d83f9db05a6bbe3152aad6115f408d972a23adb2e525efb2c309af278c627235dc04a820dc40d4455f667e02e2fe86716895bbd0466726620c3d6340cc221a70a7a57c90c7936557b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1ac33a423a9ce0417345051761e89e9f0f4790b2179847d02a33939716f059483886f83328e7347078520e535783cff601f14ad05e48af3c4dbf618f643f24ac0d0f154f01fe845d67976b76549620385e12e27c238220704ea48beb1dc1669b6faf47df14d5b23d055ccb5b43361f677eff8dbf20414ede36ef04bc575bf91d0ff82a6e579e7a8a0a81208d74a8b6461342ee6270d51e5e5ca45dd07cf1d4fb757f066f7d80a46e753d3bbe0d6e7e147862953f3b7aa9bc54630fad6156d28469ef3ab4275ea8065d8a7a14409ec1883ba4796631c85dbb1527e32a211ede5063e0c887414c95f139e3672d51d4c3ef3e14367026394c916b902ec05b9f3cc457b85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1aa501a601a75a53386dc67c43ddb18758ac6a37140e1bd67746f306474ba3cb4b3d87660dd44e0c4e0056e94c34274708ac04a033477e532400f2aa7822913e2448e98d4b5687496345f8e3740e01d74f5b766a15b7e8d202655fcb15c04a6262b969cb17d668f776d79dac0791e379326d96e1370efa150db5b4a87063ba55482e864e169b5d2a139d72072e3ad14c50ba71000e38a871243cc7a661ae2fea3d5da95212a758e62f0a479335a1b589493825fe63fbaaaf006650e62e29318441971d3b66b62a36051f55f018a71aa411db8d1e1f587fbb791cb2b50bc105d005a3847e1e2efe34519dbee86221de8b6fdaa7b107104eaf32e43d3c2c7b79e366a9d4ff15cef1901448cd315c1f03be4a6c7fd00d996fc4147581ac0f4952cd64a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06870572386e5fe94fd5ca1b087ef606637e1a2f2dfe178b2e20afe247105b3139e5fab021ccd0b55b8fbdc10a108f585b303e6a6b666b2d23d6e98b4efa376668f84b6f5996212733f909164ee15d986feb8c6b158fca4065eb4a1b5b7db94152d72a713aacc98d6d4bd2de02551b1b2f4c0a20403af2954e45ea6d2029b0bc03b93f3679ca941f2f6300ae14c3c41937969ed2628eb8642a6e891c381d7e891d40f4393c29bea31f62d30f01b46fa21c3c46be16cb3fe247067fae11d200087b97791b3f48b6d5389e53f4413267396e88714b7026f23c2911374216e4a2733bdf641b00e5a0f27a9465e2337263ad6ea08dcd7b59d03a2d83f9db05a6bbe3152aad6115f408d972a23adb2e525efb2c309af278c627235dc04a820dc40d4455f667e02e2fe86716895bbd0466726620c3d6340cc221a70a7a57c90c7936557b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1aec76d16e4cf0130062e4694a1d6ef548bc82da0d20725f4df573e46618fef250a0f0605b8445080efad3025d8063c96f3d3d3f530cc97f0d877176627a7bfb3f2dfcd77aca4e2e17c1006401cabc73121911a90c8fc85056e9701a2c9a96c27521420d01d165a92863804c6fc4dc384bf261856557b9e6330c666b05b6740e5aa18c8f778d919c478f05c2735ab2f24ef741f020b02732127840be5ea9ad7f77794d137c4f70fe0ac2342c36b3799746a1106876a61002762fe5043ca2bfe434d3e95c791b8d4a6d4c3fb84c72b79e54bc8ded4144744154a9db4c3bc1a93900f138be53562fd302d8ee6772131b74662edf8524ab13d22a0ac71f74be0e0d62e64f47542964f55e223147076e4b9f1e9edc25331f82a21327d02e46b140b112d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06721a4162621e90145e5c9e1ceed9fa61af61db5388e8d303dd25a05e98c64b1a40f80633e159334d98831123d07d6726005b0a5cc6d8484508a64e739a90c700cea3b25caff0e321e3a66172fd5e544a121b5d715b939f7dfcf83d0ea518cf2f35f74d1f32a6c368e4274a7097aac37ae94e16428b3b3d097f4b6c3938587816a048f66a2c331f7d9820dd4eef88fe1ca45b2424e55abe63a303cd45acccce760e4ee135616faa4cb3be4b2b5797293ac8639439b6ebb24a314f566bf75f50483e302243186c3a24c7f27c5f8df32521b2063b2d2668157d71e53b6c1538065a9c6b8f26c29dea5837ce60508de7fe589218fa305c55173cf47a0948db571e22e64f47542964f55e223147076e4b9f1e9edc25331f82a21327d02e46b140b112d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc069e6797018487043f533e7d0b978fb357535a6e566698143cbcb600330716240f3b84d86446b180395c0d2036b7ded86bd482df0534c5491fc8b2ca615bb84022a08dc838d43c4324a519d7632604dd529412292ff660d710b606930d9e1fd87d0c40f13c78666e26ee85206466bca37154dd6e44b97e6a721458fc2a35d72656562cdf01651e7e2db59dda42f5033b1e43c83c319f6fa7682a0c3704c8e0d41be34e1c5d424655474ae08a1f304318037de6a1619ce5fa52d3a40a095e71fe68f6e8a050572ecf5dee146e6910cc317135cb505a560a5a510972c4326c25240509542d3f4ad01a1f00c9bc5aef928063db21ed1a119b4b38f3db68186faa564b26d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06fbee015c8dea6a5ac72f0c78c89a3652347718408573b41c1b0c494bfe547924a38bdd27a7b5ea70cfef14279cd3742936522059642aaa2ff0468e3d3991e42a74cb4f347467180f7d664062bac88d0f5c03d915c097e7170ae0d969c4a943438090fb15334ea61b197dce54a925da3eb6a1dd5b64aca07d984f51043aef534ed7f7b63a12cc52525c3b756d1c9dd356fcbe5c08e85e33508eaf22018f3f5f4b0314ee494341c55ec88c4149880f1060239833326c31e86e69ee79208284ed7d232bf935c09fd3086f1ccd00ef1ecb7b4c6dc819352a96290eabfc5f86e7d038c4eb1759411d4630e65b02617c77660739a0e72d579c865af7a3e731180eff1c26d56327d2cf60535e130b6d7324c1263b76dc2701f20e14e86bf91eeb478d301bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1ae582434d2ebd24199d109862fea1585068698f5a6194986cc1c1b255ccc8c46c7233e92d0ceba3447433593ec779fb4c7575517b5ce2807de3ae843f04fe7d074b593e769bab2e28c1e3e77d2620ed484c9b351db853c414e3156d29e2e57636b21ec60a1d5c931a5da2a72e4771f223de7d82103a819c485ee97a1d8ebf0a661fb6716ba7b3fa5ccdd26404ad74a543702d1c22a6cf291e32d35774f351960ec696c611fd99c2768afb033ffb03922703175e0698694116e0531411f83b7b13ef1122739e7d381de221523aeef7c33f7d49da76a0a20a412155e36a09349175153fa725fcc6fb5ec87d334b7811960be2e61b50c6a4403404d03f0ff8e66904a496fb66b9604a0a73c863619930f116be73497136eede1854a72b48ee3dbc18d6e331165fcd114b05eb367809ba85050f037c54c8e25c2aaf4bf7286ee7c6579cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06cd682303c6db891717020d26543cbd4791d67b5d01f51b255689c6071cb9a85521a6d66f191e233f9494c67a78b2085d5d9b467e3b638117eac7b86854afb376fc06781632cbc2182bcb442342a1ec42c7467b3ac845824496c7d101eb168217956a5240d546402ad901e96be8c85a5bda3641485599f62614629404372c27435f046b5f63c2316b39997b05f4a3141e4d88d132eb0a8a590ed63b452e34c84c1d77ee6030d64a2910503e56872fdf609cdb877799c03441b00f656a718ca860fb9ce34f090e0f65e8fa9028deceaa1b98d15a2daef5294c96a75b423970066c4fdca6594844e31048ba91122597ce5fe58169208a2a8144296fa45806f2373426d56327d2cf60535e130b6d7324c1263b76dc2701f20e14e86bf91eeb478d301bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a5909f66d8babfc659a0a135e84f0b71c4e241c23594ba85774a00f1a211445424d60202182e61f74ba939f76f284ea265271ca69d66cf361833df52164b3b7763d205623396762307782207377c4b04690c0d4431e0c0c6714dc89781c23501ff57ad233132d980f4aa6bb7364f45c2b81b72920db057b3884aa73394862a3683755c00322c6404a8b4bf2032774dd07b539cc27f76c7b05ed2bb471edea3505c799a42d89c9b14d3462e40030ea31225946b00811bd397beacef5093f483c2341fa0c1a55f4d50855163a2693b9004646caf102a0d24d005bb0f9790ed4b22caaf46f7875552d134524e27ca466305c60b7517c2cdfe04620d1bb43b3e17c6a44f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc068a2fdd4af725d34bf4d25a0504962b38f780be04f0dc853502341022be7ed8722a7240068bb8842be26936398d77180fc644b27065f3053abd6af339e50cc77dddc6f1312247487744aff6618720d15475a08c6d34be8802f8ca9d1f41d0fe2f6ec5c24a8ec88a05c674fb19a399ce36e2e6a0042877cc7d5dd2771028f8aa251e338d03e938773f31d7875d0dea504b6916f61739032e5d18bf2a7363c3844fff63c95fcefc7b6d2fdfcd106548897ca7cfe92a6f35807de0c6825e93612673b217b07d6c88726a6d2b5011ce7ed654bb9fe44eb081bf23d2680014e626b94faf86707d2491252189cf506f84128138c04cab5c7cf07e4b387e2d6716906f0db85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a1e11912dc779ce5dc58343493f154c41ad68973ad561ce71dbc86b399fae7a489539c139bbb51b64a998344c496da163d10d5265a1131618b865bf72da303f0b17015959fc44c862cf11d86c44554d1d7ee40b34f47c153dc2d2e4137eba7e718af70725b31faf0700fb982939533e447b07f3368db09b673496cc6050c512498668262af27af52f7e6b5412d216520b4bef3404661c600f9835c12e5c32ce4d59ab0d260d9f763771da5c176534664620d5f836d9f19f01c90a570fe43634560bd8fa324b96c4109ba5781de6964d7c4eb33b5543972b405aef94175154b26e98ce1818f6465a004714b750020b531c21f0266ff545c551be8ddf6e867da7505962da2d6a0e2f1f2de27026d16ab47a5d000f4493df0a04d0444c0bae1ee84af467f723a3cc0b75f5992c26a24df373e9d4a67c5badb5067fef4d3ad84ea2097e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1aeb96851bd50c250d28209742b21b1a234deab85edd29534e7ff8196534593129f2bb661be0b7bd01529d9d314b23e20e914edf7c4c95491a53923e258c3a4c655940606574a6154213feb4541bd85f0111bbcc52fdc6896820fc52154c0c9b6b2dd9994ee906ce415da38c3a69b1dd0b71a0b6598bbcf92fd6221c5569fbbf6e6d845e6f72be400710038f28ecc98d31b44cad77a14d6f5a2e906b678b5522727755a7171a59822d390abb34eec9034bdf36bb6b01dc983cc0e4cf49d182c167e9a36f0546f8281c0d79a4486f017141e7699f46451bae3a8f8f0222b94be3273e1be04d7863b23b611a652c33b1e110c6293b1dbd258d263b224d7e17cd9a712e55885f298ffe194e5a7a447aaa2448bcefea1d367e406f72a4975720bb45058358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc064cc7b8616eb7534059615561b9c0f36906618466066ead75be5b3b1f7d6af20593b3e4105cdf6d10b8863d60840cb76b3f510e4bfc42681cc9c7016b9fe22770162834599565b2259f02ef63cdb31f1835fe5f6ebd82c161a5f0aa306b9eed1af9bba87bd2e1d439832cb73f6a7cf7375423dc1e617d795d2cc34c145891784bcee49c55d3a92678b81ad826abeced699b289b779a127d576323864b5f50f56b8511b84826b8597c9bd6c64916d9693061767f73d0fef30aa11884751496096c53a20729c0899a2ea607de1fcdd81648412cef185ab2c46b51e4fc62f2b2d10608befa71ebc1d435c0f79f3ca64a930660da1717ea0dde789147ef103c06b315186cd66c59bd98290f92b21821adf27a56583b5f1303954e4a3be47debd2c639f667e02e2fe86716895bbd0466726620c3d6340cc221a70a7a57c90c7936557b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a2ffab2758c72070ca968196702a7831e98ee88438321f92e10f3471829e75713faa1920d1c115b47525a7a5ed880a51151b840721bd4d25180bc241fc2e21754df45392751a9221f24957507c1e54b3c44954c63edde0f13780ee432d84dba1900843c723fa6f442d45c3b1a8fd4d828ca52aa64a582f55d758e4005168acd516658947667be7d56a73b086ef4fb1a4a65645d13c8f8282fe1e5b922a1fafc54bee0f9174714442a7f32e07a4f04417e8d65ab5e6645753b0d386236401e1703232bf935c09fd3086f1ccd00ef1ecb7b4c6dc819352a96290eabfc5f86e7d038c4eb1759411d4630e65b02617c77660739a0e72d579c865af7a3e731180eff1c26d56327d2cf60535e130b6d7324c1263b76dc2701f20e14e86bf91eeb478d301bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a9573176ca3aa0207376e6b3d446b6b59d8086c4723a51546587739130fa4be5d6eaa2c4d7d63771d1793204111fa3c243f181602207e2f0477f7e52655113c2fe539356c4b7da04ad6e5cd44a0b95b05e3b039609fd41b0df404f21ff27c5e014880b165426a0a220fd2101307279f78d6fc6f16d618f67aa605dd49a827ca4d8154ae75b41d1a6a91683c4d94a70819a6e7d9443d483811684a9b0d95efa53f1bb0c20054bb43233be5915a60cf486545d688407b88e15840a3eb01fce1783b69b051407f62982aa53d0a32975c893a01181a1d475189379e006c1dc4657103418efe50ec98c6734e78f2503f53525732ad521e81c842088e1c221e94ddef20bdbc02148df1791ace83fe277a36356c6571743834959d256e9ad1380bf1e5318358365216e0d120c44bd66dba1b97411bfaf96cbc353a299e3ebd29b506ad459cd71b15ee501f59c83c5226062dc42592dec75f1574d479330b294e29c4d64e777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0656a60f075a494842611b293dab21b34c7d24d55155d514339bb6b81b30a6a6473985b877e13fac1a9f387d396627c650b4f07b4b831b0b1d1068a95fa9e5db7c25948471d600db58beb7d762011b7a44f929170ccf1c0c57693e5051bb10e724b4eef1019ef18a12293405386985cf738864984c169d26134b56680c452b763ac2ba6918edcd0a10442aee3ad9ff085f335d73624fbc02695cc8da3d82598a29c6e3e017cba5a623bfaeb3630360e243f2fa9c383bda4e44a1a4cc21fee4b5152a34ab0e54c3fc162499c0518f5e2a726c34eb2342c287687b4d3d70bedc6b79e6e2cd29ec4c6328ce94dd27fd772d763fe55c202a723c2b11b22a1f518c367844f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc064838e750d80a1a167528ab68fff436360326ce75909ee95db2575022113a0d0c946930366a0ec6278bd7f113f56b174ac854bc5b5b41ec05236fcc1b3dd8be7b572e800c33c2900b91df2960c69b643edaaebe0db8faae4659f08128d68938448cbe15084e1da83257cbb2219f34845490f87c2f077eb75c5deba1325f82df0cd6af4718ce7d4d39c8660430bc4b411d1956b07d86164573508aaa6084504e5ecf294f175ba6276b8fb9a520cc9dc04c469b404f5889681bcc3d3542cf96440edcbcfd35c1e7d15dd856be6faf0fee16a3c6b30dde2a29518f15f05a1367df3749c123383dcf5b72562c6914090d5f74b4e0501fdc43930feb9f396835c39e79a9d4ff15cef1901448cd315c1f03be4a6c7fd00d996fc4147581ac0f4952cd64a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0601faaf2d78df8e60e7f8ff59607d3f268dd9646a428d1c7e7975896ea6882873c482ba306862f836b9c8aa5d56f0202eadd3fb39ef0fc4509a91aa4abf75dc17c5f18b1af9155b2905f3e505a9e502253d75e26a5cab6d369205a11767abcb054b804964c5ea0035239f2517bdb43a771672da2ece64c6074dc3406997630245e1bcd93321cc382b5ad5bb776bc06458bf776770b498ae6a5d421c66e2cb4f5511e30c6e1aa05b67ed35f450ba9c0750588856168ab1e802bcef551942011b7341fa0c1a55f4d50855163a2693b9004646caf102a0d24d005bb0f9790ed4b22caaf46f7875552d134524e27ca466305c60b7517c2cdfe04620d1bb43b3e17c6a44f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc0625f00c4b20e5e04dae848963daf8e87d9b48f45573fb6201717e66525cb3e177932d27350f1a2d18247d3738d94a895c293ca570a8146329e0418a1e7d155d5ae4eb085b1d17353813043c3df20e343794f9295d1d901b7ebc4d1a07f7ffff68e76ae2774808b211593ba4669e1df9233e79522c033aa233c8c5013b57c65702c1387d5b3a46746c578453481c620a56cf70ea51480d382ac53d2f6be6c43b305916c31f46c1534b5ce1de70afb1e91a28537419f5869b366432103ef06fe80f2a34ab0e54c3fc162499c0518f5e2a726c34eb2342c287687b4d3d70bedc6b79e6e2cd29ec4c6328ce94dd27fd772d763fe55c202a723c2b11b22a1f518c367844f5e0107d445b214d9fa633bd4ed15f78eb670ff612834a17c02563eae7d058078855695aef734828a7f060834f1d591e50353459060905ebe8c226290d772c85ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc06d70632652a41552be22ea76ea2bfbe187d9ce01fee008e67b2caa9504591840e90cd6724395ea6544a2fef4debf4372066725a20af815f35c2b7961d7559364922bc592ff6af6f48f66917427126a709e60bcf3e8ba61e49e0b0c2635aa70905b387da22b68ae4390aac551d46745e04653a04655a9f245f27a0020fa142625e4e981d15db7fa10d0456fc660323756e7150ae70caa64e3a389eaf698cdb1e2c7903427dad39485b077914321a53fa3047a45219e0fcef2f2eb1f5445eac113a0dcf321aee2ad87a7bc84d4e9d25f1440ca31d38df66c32d9636f1023dee800eaf86707d2491252189cf506f84128138c04cab5c7cf07e4b387e2d6716906f0db85f0911d27f77792932a82aa8ddf74010cb3b7a7a80780538d6e51a5ea52d431bb72852d2d710168af53a3173804e079d411f0d52eafe312658cf7ae26e1f6b9c11745cfa141071ff1f443655c2d517954f2234a34c1e6058b0c50f1f83141eabbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a2818405e590932188c7bcc623316b57616896b27ace9cf711833b642126fa6026ed54247d55fb4273d90536dbec3246ea5592c6d1456a156312d3a4f640b7e2ada3dc01c69bee644254938741dbf0034043efc46f805595743a1643565c9893b0c40f13c78666e26ee85206466bca37154dd6e44b97e6a721458fc2a35d72656562cdf01651e7e2db59dda42f5033b1e43c83c319f6fa7682a0c3704c8e0d41be34e1c5d424655474ae08a1f304318037de6a1619ce5fa52d3a40a095e71fe68f6e8a050572ecf5dee146e6910cc317135cb505a560a5a510972c4326c25240509542d3f4ad01a1f00c9bc5aef928063db21ed1a119b4b38f3db68186faa564b26d6433e67b49d3a28f252363513975d377a654da89dca46b7f5006f9db18458a445e36942d3d46f5e026d222518e309051575501f646273285adb48f1d7645085ac374eab6449258f37df115c048335cee0e14560f3766e57189313b2b48358777e2c25492978473a46960df57ec739b04d755edbeb216950a7d90c1d68bc064880920625a6c836f8fcf03b46885717639bd50acccc0e482d828f2fa79d562a2dd9952f63a9102c62aacf5da36a911a8ecf3f36b09e605ea727b96082f5e257493e4e144bacfe263c051f48b3cff01c76227b6df5236a4ffea6132533b8a7719b943d3e705c245a734b4a57d9ea4408eda0af26839ace44d99cab70c85d890162c2a02773d3f421e5857e642cb51b745bf33828b51826722f682a7a8db704766b40115c2c2f0442b971ad527918e576e53ad332439a436261ee355dba20fa493dc1643eda0e546870bee7146194bd1a7f06aa5dc2ca6d52a2df0e6579ee671b9a86747c5602070020a605629beee921b24ee52947e9245a81a03f0f4522b05ee1d66b3da16eea1a2e7e8427eaa1574bb927b561f4e3fb771ccd4f3ba99e2317629a943ccdada05c9765205b5745f477582c574fb2518458f5f6263f0f4c19537e96060697fadb4a85df685b943d601c57fe5f2d20a3c06e694b9b10c1ff1010abbe344084ee4b14fa729758c369626b1665d26826695c43f69731773a074c1a38c3a50dbf58242bec540f40a255bf070d01cc3974ec9b059640dd5f5a8fcd14ec4c3a2f8ccfff6d2a97f61c5886431e116092327c5d8705f8b8b615bf1cee76bd2ee5634a522b3cf7cdb109723528677aa2cb73dee2185aaa58211c9d94b035fff2126cfc5fdf4a156b1e72b97a3f212e29540c83ed00636cc3bb762543835c00d92b097b4df470b717dd52169d7003eb8bfa11ed614756b0712c7d97bf25122aecac4099d2d060d71818743923c40c4db2da1ea0bc850e5d895857c414511fd5364935d5740b489242ea0089574f62a6db2416d4f6a042bbf24218a77f28614ded9a3fea8d5107c9bbd70bb1dd87460d077d6e2087ba64c2221617d943631d2a2ab972b256a43caeb82f431f7951040ca0f6081d891525f685dc4f1c3d0d06be501f0a05b1612ea1ff7358b70a0f24fcb20f3839e21f58ad597a41a7557034ac35904a8af182414451a4617ec7485d23ed617dfaf4ef3cbbed076697ba3a65f09c4f3414ea7c60dfd9a443f524f401cf9821575faed548204c21117b54197a0a76e14b886e87036a0c6a00fe05f33441b1242f50e6f2239d0a135183ac777a5aae7469f640291ce47f102df902ae1d7172fe3a0ef3412adfb62921c7346f43173d6f090db90a2f75fbdc58b3b5d715c848ab208c669448122868268eb81c505a0f011062359f686bb5a85b9144f94f26081508913b54282075980d42be8b0563b4506fbbd2136ad3b2b77067c2937efdbc00692d70606576960259eaea3d5727a4aa47acab4b2f3289f7417460d22391d1843a5c47c927972bd37631761e000c0daa062a0461380b01366dc2394719393bac0ec372cf218241fe1fa528ed591950c053c47766227788c00f934b795820b1150581e5d6744c84b27d6abb9a40183a936ced263030de81de57cbbdd21076519e7b58306d4ef12e490d1a73e00b5b03ab0c6a382c420c7dc33877d3534784a8896a900fd51e9015343617aefa3ef9b60346dab9625935873d15b206c97d215bee38c5ec8611710c7b13781d661a6f41436c5209e8215e5f702c8ed7f7556b7dda7b0d9ab7210d7f1e571ce79711471f2c06c8987f3808034a3224fb5b068bdf82065faa5233013b1b16f5769a49b4484e6bdda37e64bc748e1da3267019f8a102086993fe1d154179222bf261093aecc276f4ee9a633dc5797be8b2730c9e399730a4a22845fca2bd3b7fbba025c0e84a338a606044a05d2144164df52a48ffff778b4c661dd41fa70fa0509079c2da4e134ffe440986e6200b17af4678d48ff55f7489ef5ef444480f0afe936227fa9734225cbe018fc8da40c0f50d0cb175195513b7da7c8da7774a93e2247985293015b4cbae3775518b41c128e214855c174d6c253e3fb4010576798100236f737b3bf4f5ad5260dc764e4d429678987028583ed3fd685fa3f04c66f8033ef94f9a1139c5e441f2b721318fd4575892e51b2ca05e63703f94634bd65de5369efd366b864cfd3a2f07021b2956532f436ef97b42caac24bc64306ced2a2c4871db326c2cc29a4c9e123115e55e7f5fed466e485e84800e2b95c2422de32913e68b936f8729e436b0473664f58b08302e58672e5ad71e39433f6a62cc51b11df76a943b11a2456958b31723ecf8a4623d58dd2ea5c9ea2002ef3a2a3cb8334bbce8bb45558117208d2e550123020b307dd0aa45bb4ca6774412306b56a384394f628e00a789a2732cc6a9661b1c91530bb1f754e7ad8a651c8a490d31c60f2395d42c5669d27c6b3b4e4d56fec7ee625869a87dc49ad33999c64431fe21417c134df52496e3f1152441b44060b1ee6c64921f6ba6232c42a1f51c6461f9d70c8739945cd02ecc257c074f302f1f07201507581cfb4f4207b1298b4a1863b70eaaf93949a909ba3c710fa477e40f121e3fa62a5465f6c6718873660a40c01c45cc39ff6939bc584646e259354957cc31ba7ded254eac2335da38ee68ad297c796bf72a643d7ea14b10e6fb4efcd54d666aa7d71d6d47420aced0fd32fafe51727c9b962b089c2d348cb9df72bc84c7501f468c126250461b2e7fbd6918d7d23a34b6b02cd538e81f2abb8c1115f9b51b9f9ffc543037513dc2b80d313a01134310618706c062762c2026f1019a657a5d147616415db2b7288aa7644af9f719319028575309047b45fde2855ff6cd24482cbfc960129f96321638a178bd58a20f1917f47e46e27469469b1a5263bc611466d3e81d3296cf000dc7857cc1e29953709c0b2211fa422a1b88ab6269eddd4ef57d5a4f924a837b4849671d33c96a5c4f04ed6f6f55ca42fd8d20283adbf812386ec359698c7d681cdb2805f3f41353f33b305847724e658798d1176b054e18e8462350f882562ed7391335974b6c337e6fcc708490b818074e645537c72e7c52f73316e30da65ebc183733b228bb24554c4b1fad7d3f6ae4e57769c1a7df3632654a2bd6fb0e2a3fdd6b176422bb25fdca1042b1d2a14d87cd9f7b33234550abf3bd3c1f7b4a766637f73913b4387c1b571a0625df14560559a574e48334594c19904b12a02843c82d570a7df3257d4925436480e5ae0293056c7d4c9b06549ad735374d6b7c63ff9c89614c3f0b07e5642e15a6a88a7176e29d2a6003360b03f38b56a1a39d770d515b79d3e90e2c47bc7160af71b8095141541f72e940010fd9080db1264b571b681c33d176ca01ac20501f8c19586aa440cd68b79e6e5f9a93bb7cdb999e6983f25837546027000fbe825c925a5b4adea51e3abb82f7412b8381477bf127722789ca71ee85d5140accc017ca697d4b922585538566dc21b1e5cb2e7613993a2ca59b705e5dab6f5e373b650ca8a410860a3775d744252edf83ed1f3ab20b5fd6b04023925c7f70375c7b104242f9017634f940008fc2697e50fd4b5762b027e94b100456ceee7443608476fc4b7e536d2edf4479ce1e354351f775188d8f0b4b92b26cb122b92a344d1e0ded94973c510ded52037dab5f2be60074d95ccb7d4830a10069e73a71aab5755217616f434ff3a80baecb181df68ffc6500722f13be2cc541070fa152745af31f3ca86b57f50cb25a694d436cfc61dc76b8e05e4a2c8f412e19c5fa79051e43382b554422e826965496a9494c357a1b5308cdef5501f9a0325bab5c0a8b5aa81fbe82193776fd71683c4abe32f67889470bea894b3bdd5314efe25e2e3110f6648b0e4a2616b36e147be3ec48e591de091925420f01d1d57da69b4b605631ef42885edf1d2f9fb16e1f3ea967a7eb7d1acad6e870e7d766775781214beed4ec6d7992ba736fa1972e70509d3c0fe78a7aae15613635635c30aa5ccb61e35d65503f453a042ff1be15a62fc510e362ab2abc8f290c5013fe30296d0e408ce825263a6b082ee67a0a70e95784681486f24db1b987716a82b904d5b5c7212b2d7265cc8cfb50e1b426289aaa6d65d3a03e27d0f57d62648644424402c10658c84e25c8985d1133d4ff4ee7f72e0a3cbf05249fb81418b644d0614390845307a2112eb0b7a03b7d1f7e43627245237142ad357c2a5900927220589f47706a5e4da87a75f4016bbfdc4151f6c9b312e054716cb01d125acaee3e0b3365c4767614e56eb52afc7de0e9a90baa662660add0017150136a543866b920fe59b92d42a47c0318279e2d2a86066cddb67a68d5fc900619873c0c5cdbf412f0056a2d201b1301e18b2045ec276866d978e44783210d6db4896472113e81567b37b8399b8cf539540b8a6d72a2570f246d7c1f397f3d2e7ff9ea638d92127b7d0cf910014b761a7124da3e13fb0424fd01b412d426427cec0e6b36f0464678711a440d59f0ed66c5aae202a8eb57696fead9625a583a146deea7678f50f2748b24a827c7308674fd1b6a01ebb2e8757fda7e0bbdc83d76cf100d01f4dc49206a3f1f615416800c53f10c217005262abc07802c04d86b2f90374d794363510dbf4ee2049319602f7dc3d866f05de85c0ae2e921995b6703e2361761bf117b7bdaa4b051c992fb2cc739623a0a63267bdaf9162e9ba45946cbba331bf8df3b0aa06d7026dea13771fa5f95052d680d1c7fe23801511f3e5cf3ddec7762748f2c66891c4c9ff5895353c2e46069b1993cd908ee397224661d4f183c09b6d9b662c6c9ba64daaf7d33e3031322171b023b0fd39a6384d94d1e3d5f051d6af191772fe7fa6fa124cd5228357b6bf6977c7b03e3e66c5f57e123a6b4ae6a050ccd7d3e39541df05c7f47061e737bbe72490fcbd07811653a2c026261903c34e9ee126c96b6070246ab1ea4cb177332c7bf0bc2d1373b08618e399a99246dc04cc234ff972b7585790d068380d71d5c766c3b4eacfe39b226144aa713fc6fd2cb83739922a447caf47c43822a9f7eed80191b289b7e1dc5dd3e1e54926c136196881d611c213eace5816d2f5e472336820637499a6526073ede1ebcb99423558eed6d5b1e2d06eda2631c8498637e7300fe3c4b025d63392d376a5a06fb0c82094d744613966b6377f0141f347d2500654508c1dd4a6b85664769c4efe16b4b8d3c14b192a53fabad5e41be64b631d81c7a70928a3d54ab5f9f733c82d42793424551d27eb601c76fb02c96d6aa11ce23d82f17bd543d956abb3416bcff5d676cfb2f4c8991747b723a6ac58bf85d3ddaa0789e5b2f6ba9c8c82978e4b159ae266858a0a4af2fc1092c1ef42d0e3a0ae66e3b0f84e81d7568b63a06400b427371004380d8f027c16da3400948c721fbee424c8642592e9487a246e90d422f12613b7e8405c033b92f924d85cacd0de1d52b09bcc5c1758c88b7096c20ad4dc1089d53cd021703deb2c86f09f1176f20188675de6b8d15ced90b0c8c17fa73f28ff543470916398cc7cb42dfe95e321fb5b8235982197a87816b69cc85055926b1cc3339c3c03d785ef528aba1ab3e79e3ae43cbd1ac5a11f06135c193a44f3fc6842b7e03707839fa35604846b6138794022edc80fa4971fc9f265ae89068e2c91f119c05da65d027c93111ab426670397f6cda44b370ef683d557175dd3b6cbba05b725a4e16ae77c55370e3ad5a98de2b28bac21a103937f824ad69f560e53e9e7bf28a5c49cf5f8c40192ffb4256fb230aac4d03426e3dba3689b59b592db93212e5635428b75ab80631d7d21874ac1e39b27f2874b499251ec8f1ee662fa3c4489f9f6544772f9b6a4f78d70f58f2d56fc912a6169a9d1e6683a89a6ff378de687d44710ee997f34203d2eb6528e46a35e9b4c15d31f57211e3ae66676a5be30665b17d09f044d237b2f99b043fa0fc4fa78ef1363d10f8516b227e618dd9c63937cc513e4dc2e32ab11223222f4e5912b155fd3f3370aa3241d74a599f3cd56f17f7c0194e511a2b4b1a23444e6f2f3a6a421b0a26cf2837c2ebb901fe0ade48cf075418c570011b27c7be67f90bcf17679a4c4831ba0119feebf06bf070e5570350d9164c603224f897c02babe72557f430a137704ac34372d15625dcfbd341312e0376ae3b5b23873c832cbfddd12ae0d8722ae80f38642002ea097526885aa9159e63e6673958dd1b3069b08a9c47281dcd1d0635f0694bedbb5fe10671598f947e0dda201c308b6b2f2086e12d1743b24b640611411eeb3f8063f119c57ee102f44aca0eca37a8c331276995971b319f7638ecb4d46a2c3de154c8c52c2961af195a6fbdde7095b9d145e9c109759493f043c55f5166aed8990585ff765c7c20e229db20b5059396e7360e240906fb7cc655dadade221d3c990146b2ee0e2fd2ae205107e20afdf6f072c197fe3be1aaeb4733c4f94859e527495eb28e189d283a49bb62923ff78c945515d0af2ee99dc00d0871cb60607a9f59677a9826118bcf52c43bb03158ef6a53cbff3221f6055f4879533663701ccf4785bc127744be5466d2db41291e41de59182e8a445f9f7b196249d12230d77f2611ee9544088bf866d16ec2582f81a5141a014412dab6041a4cca4752bf70c128951c742c5bf7a41e02103e4b1127c1476af17442c3670f4f756d69257b5f336badaa7b4e0ca8c2212b2f6817b0d74310f01f745bdae3d8753420765433b69b4c25977d406832d86d43b394118a646a45e79deb14372b6b26814cdd374d75460991121328f3843f305b03cc5cc56a857bb20aab60b79a410e893cf0226937af7bbbcf8164c0b9a75847fc47729221887b12d663699e6b431c2390470239726108df1e0d6b43d1e225961be4013b37a34c51e25e2b8aa85572effcf96a0f081c789897a72b82c4e927fa0bdf0c6666d9387caa9d29b4533868d365e8160395303a9be40945fb67ad07d7414e2c59e6c84c13fa83406a4fa26246558530ad0b6366f6d12429bb201e76f817026d49dc160d474762491cfa060089ee6a5ce6648a5bc688c52be55426690b21a465064ae019320f6774559ce646bd3e7500647b4f51f6877e3802b5246b8c1d474bdaae6c1d26ca476d0069317458f0682cdbb7413b742d4526216dfb12b9b25471a43eee703cd7be3df4a5de32525dec3138bc1e7968ea8b5aca4fb460ca0e7b042248103bfae44c15aae1846d4336d74c3e22820bf7bc826ba2532a783ea45020ee756e7d9033512d5d9f9d7d496362011559550337c93a19d5518d03c227bc56a82eb14e9124584ddcfb1256144e540afeacb45affde5f37f097924ee1aeab5ec89a5020e1ce00099faf12067b881b3cc2748f0067abb6576e26ac5bfd508a0a1327222af9aa162ee28e6f7876e0c61ad2aa6e1ed1b0d24fe396e90ebc444147309c3064739fa42536f8c13da5673f16296e113ab54b3729079a4c096518130da6d69a0b88ce2b38bc8d297427390a32f07baa63deee58106d3aa2069d62055aae34cb5104e74463d895196ad0dc033220500d2f6b3daf3f980da552e4c1374d1de0b5272926027246484456ecd9762c19239736b54b962d8dc94514199ed7612458f707e9dfa628e3817b2fd20f2d366f53b26857226a7dbcd5b4677f36f2090460c91207ea7542849b3f7cb95bae7da8ae76106cb4d840509716462833ea165bc1b32318e326198b84b453f2365665d2716b6780981f55f691307e7e922a16e8c000096be87d4f011e2d16e8b7a467a8649c5ac70940134732132f9f5a3a03ba7a380486c97847d22a2e1234aa6e34f56be9166abfc769d92ca33f9b8ea85ec042a67d1f9223334ccba4528b50cd4b78773020de71706e0a5afa5a7102ff20c4341a43749e534b91e6fc7675ecdd75bba4641b2ba70642e066a41452ac6c7aff55975d19c7b62ac2dbb95badb6b30f690b2b678d948e0ee46e8208358f5f2f13896f5aec70a63e4c78a612876a586dd8f5b137342f7238d5753c39e5d5c30ea4973636d7c77f79dc4bdf5c5cb65e69c6e03f3018fc9946c0203e7af71f7b328715dd31bb28d673ec5a7005528dce163a741e65b2793028e8c0472683d83157787fac14cf11a303f40fdf11ced68d739e06355d26effc1363a92202b731aa04ffee8a3c0e3d435db73fbe3107635446275fd21e0933b30a37ceaf1531b4874abdbd9407894e723ea693b325168fdd56b7584d6d619012728606933394a67763b058ce17fb0247258a049e542ed3994c159f2b1a8c665005434c900f88ca4669774d6b6499601600db7b0f520c82536a04e6f3686c99d809a2118b68d2f45709ab22f16ec3073a2adf24126fbe545c72cc9870290ecbd2370739c8226e58a26287a3dd5b5269c5673d5ea95b5199ea059315b40661fbd0321f54992f76f4757aad3d89742fa90a2bf7b382455bf1a836b1c2c57430b38c15f19e0220af0ffa41160fa464ee642b35b2d12a0c6070ac3e404e9c56c88f015c02b3f038f127743705ec873467ec9915b5396339efcd183adde7e60d9741de545b11802d980edf3c46fb6b1e116adf2a043d2d5772e1c9694b4c4d6fd70a5f6fcd65050126af0c53f80e01613f70755962ec69319b382f083549f56e2559b71a66c77b0b98f7a96e76a477034f0f2c01a8f0e93fb46bb304ae7b5a7dd4b34440ee4c1e6f2c91151be503997b7cfbe63c3105e612e6f4712b9ca60e70e835133f595c705a417ceb543a7dfd5f533db92df1bbf1235c471f2c56089828bf6b3a05217e544ceb994122ab50bf60eee9d3550b23c0644cab9962d0e2a94b6808fe60cd900c21aac2357b2a2ca4201cfebd235fca2d45e3de3a258b274d61c418cc748c396b0eab415838d02fd560bf3e2524fdd80e776210836f0e5da5121487f52cd709ae20bf9970577f529f0dc840097d688bcb6b8c4dd15b34514d75d85f626634bce8301d21fd754f7dd44a17e0461391b4355248eb54015c80d20e84decf33a8deb02c87b7b33d4096655f044ba63023fa2d511d73571d8823665eb5f1c355756ef856a6efbc71d626ea067616611882d2765706b0c818b51c7d188e96db0574db7d6d88f14b136299a550fd043d4f408a423288a3c5099f0bd02000dfcc2bb540b238d8d0b15c61e1d63edc3b451740b12e7220ad3d6989df6766b9a4811e5576b2114b8d31060480a96eed5e2160a647876cd6bd2815534ea6345ea1354e8efff01b9177b436dec83d282032152f0eb6055d66cd9453e7aa4a2bc388c14625b8df1ba0a5cb7067fb6a7875a1f23362105772c78ebe305f4b5d44ae848771f9e8713680f53b73b0710b124a31500c1bd8ef3f2fac1843f3cd6979f775b768f8059d1f86d5e97d0610b62549723f6d904488203a004253cfea351aa862206358ca162e8456807ad2d4c86107b4f33e8673802f549f364c9a893f5e3b287b34ceff173547c3b6553384fc21ec8e77130ce6de1ec30dd13771116906bd29fb084b352d5d43679e0f6f496c767644ce247ba29375ce9a56769f23c845b3366931be262d2cf087d61b36a6473eb403f0236abfb667e962e357d486e26cb9fec357ba45eb6b55a79b4c993db713f1f5e11a99d2d76d8f104e7cd1706055b5c34649b494da65b25edf01a96f2265c390c605afd414333c39a0521a80842b73a0bb2b66f259044a03ff3d3f76f37e7aca7d059f94e869c0614950d361526372ab7255de5bdc78c6a9f90dc19a1629ba0bac53acc64c1bf59db701d07dfa3157a17d3469aa105bdddc4165448a500939b85f30bc62e57ad8a1f651a45d84652bc96b7c3e415d176de1a325b4c949048563245d14173e3a8209570ffabcb509e7fb7c638f752c0703abea3eff9b4f260e362d61f3dbcd07e0164c5e106a4a359526240daf40b22392f3814828b12c35d0a0c467a0786971cb44cb1600ecde174ec96226e5752156e449c209b3c31b155f1b881a868f1669a67b5d054926be31c626770dc7d7156e5affcd7cf1daa02113c35d17834eba15b02a084d8e81126d474f311e96824c2e46d33c3f89feb32c588e737a89e3dc3a8d243d01a5852942a6499f7b3a318e11ff9ab70a19cbb91d6dd3b55827727c1013b3997386554a382553d8268dae0a10befb3238048745525289655b81f613102a75b533334f6c6ced04ad4148743f67d1dbd1070db64f0be34efd2846d52e5bceae7709a21f6e7b6d86065d37399d2a7f6484135df6d3484c54814f8467e460544b7322da0e344008e6893b16a4b342834c636dc90d5832b7c941368987785a2d7d626f03dbd832d4433909302eb415d6416e3a3d95415a230ee671a9e9970631316d6f51a0553dab2a4668380c92029557142e84bc0e43dc627b0677635e3007d451245eadf37024e6a60f86894f5c6e82cd5fc62a82479c0dd720fd469315d90ea01d6cdc793bc5686225dd1fa216a875927db974b13274eb1223a522be64fd272209d08eb600f788b34d5b7b08194ec661184d4ac2183ad09b412fd72c471e8c282a29744279a4405f798eebdf574fb6495c1ba513230bd5de44d06ba1460f7a1154ecc5416fc8a98e185bbdf01a31dfaf1185ed895a8b54702cbc497216b98fc255d5dfad0d40285f7875801119815bfe4ea1be172d25653c531f915c55b53af15387285d388d952d0b99080f44733bc91d483bc158a15cb721f6f7875f27d11f2b6e01466cf13ba436b5b6db4af7646b002f787863bcb73916daae3a33278adc37d0df863800389e247b92354f83212505c8071755fd9f4605afc7c16ec4d288142235ad3e924d9a4b8733706a871a9b2cd4bf0831e3da073384741a14a0989316bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a717b367df09d32185b9e7509cbee9c7ee7b87c4e60179c207556c84c45d1944e6c24cb5df7a3477016c457686099367d3ff46866affddb1d40f67d74133cec707d20ae2a793acd6d3cca1269cf022a1ed38ce513d7da2d417bf01a53bccb62655b568a65e705dc513806ab085ead145ee260d853bb7c3e28cdffde04f0cb0445c07edc43dc965e6209cb0802ba2d451d44361f285256206a9efc4e706a216f31d14255684f9ca35c86b037649e9f6065c23763615d5f49371faa521a73324c01cb7e636d854a65426415be34ec420a62e8eba0128e4e83307fe14963978aac02f536bf104f51fb62a7b61f4524d44a202b4baf087d2f8278d4be6a4eb5672535deaedf5fb052dc43b8fec03d00b74a047edf2a2ba0acf00761882e098e16d558ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e60b4d7230ed7b351f4dc5b474d16d838745e403e9d0ea1687772ae35734b2f7a4ff0e8367d8dac5854e40b7ee719014a8a82f762fe91de6f92cff00061e4320553390e226279f25cec6d5406b40f751e38bb80649f2d2a4b020d1b6c65c3e81ea948033cbda99f5547ea003986e42c2b5f15516e8f27da3c99b1570a186753407515f2109cc90a00bf767d6ef64ebd4a72f00f47a5e70a7c1820e46d3102722c0f9134178aeb9a3ebb0f857a84b24d7888d4b46f8674a812da946e68001baa6d1afcc161744dde6805547d28c7af7a3c8939c3426fa58106f0b3842954efe12a119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a675c4d459aa6454f07021b00ce802114b85ef3152cad650c8f7d575332e60829dfbb0d1266fe2e236ae5446efdd4b348472bd43fe9e8e67293fa085c5b14c17d4cec200e7f7dcb171bc0ab0c3eb92b4d36a362274ef081376f800375f5ce7f7a2fb2c41a2e1384642513a9584af2a33bb4e51324f3071c4ca8ab6639e1dabe1fc1c67f58fede6e5a9e91cd64427a7a56b5a1624d47370d1e6930aa22ff97ca2c9397cf46941dbc4a0de0866bd4184d639336b819165c883d84bac861362d512e7f817c10178f02653208166681ac447380a84f3966866f4310b09c4e33b9f464211fe3320b2ff515f7fee4253873e9328179f33838164b2631fcfe062cbdc476480afb1a9327de300961bf17f59ac955100e770ef504e867d22972217b17a4418e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4ee6de4a7b2f93dc13c35cc91b590ad80a74e5fb4915175d3d16cdf91be41733005a2a0f53e7ccfb0c78008366453ffc7ec25c6a636419bf2d6fa126771b1e3600595d060633026743807efa0ba757241af806ab5d4af1c14a366802319663267c1bdeb9724b24bf7b12053445b4de200ec5dc2e2e049bd513f1aa1e6226905d3cc63c8f7115ff534249aef463c4857c67f0868c0afd74731c362cb5687087db0d01c89051821dd34d7c6e6078991a507e939c7674f88d02796c887c0a856a3f2581b9ca43397d8c1e5c4c1465cafa5b545a03445296934327ad87b85bd3477828119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a46f8100081bdbf6ff409b3439af5c0085556aa2b63b6f17037b9e3276500a01a1791f53c14d7c8328f2c9952331ccc6a6e8a2515c088271ff5252c0c35a4ea4adf711f295a431a09366ca048ffa0cc080073465a63ad155eac75214fd6e3cf0c57876b7af4ea08388e8f3314e794130759db2157006bf6307d054a2c9bc5a9326202a65206d52a055f77812705698d6023892302d9d40d09943a897a0130360bc0279f1f53219478d440963f1d36f13a0f5932290e4b5647e10e7031eb420172ed4c2c036ea0ea3f0cef4c64623bb8592ce9e45e28dfcb044c20e23352ec77270a5efa6801f5163eab888b120114520dd147ed2cba630f2056c5995f1c1cfc54cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e1e6961337faa625c4e561f03cf541e5d8124f6244d5e6650e20e6b641474945bcece01324600a541f20ba9551247045a6c5716593339593c1037ec3e1b980b6c5bd6857c71ee447d56edc8517d3e4a50ea29702d3b64a656cec0b24596a49263be193c7c87ea195b6a35c11ef5a19b5f923c0651bf4f2e71dfadb6372356b42ceb789e3c7ca3d875107c982e5ac1f742528abf4b3e831165650d5d247f552202441a4709b1be0111587dcd50001bf20878b64039d999c37bd8dbde267eef81053649157dd759c71b3435513e3fac653e28c5ed4153d6b825a12e402f979b6422c3cb9b6cf8502c1f5f09701acb8729669abb09159dc8757504a71c6df7a1f61dc6de225be0251e349872372a71771c141028c61f2083546aeae9b453cb0d6a797b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21aa3b15d03537258030b66fd0b93efa44f5cdc1f768f461376ce09383a2412375907c87a0022197d62411e4f48d5485178ae0e651e7440cc50d69b494b91428247e524ed190433650458ad8120a9e1f8315930c41537d64c426d12b532cd374b2f11390927b8c60b7a029e672c0e06f40c79c44e3962c44e4e6cf5d64210d5bd474141f016aa66877989432d3a395b9308a443882461b45d77c40b2a7ea27c101b6e01466cf13ba436b5b6db4af7646b002f787863bcb73916daae3a33278adc37d0df863800389e247b92354f83212505c8071755fd9f4605afc7c16ec4d288142235ad3e924d9a4b8733706a871a9b2cd4bf0831e3da073384741a14a0989316bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21ae358d1515356c71265285c4813f0a269207fe71ed5978c2109bb3b1fefb6e81eaf9ffd61c5e115264b169b205fc64c19642dd37d5d1d93276a2b4f6c8211f154c79a5b551262736b176b571093bf9903b62c3564bc43ff168b099c02bc950d1811390927b8c60b7a029e672c0e06f40c79c44e3962c44e4e6cf5d64210d5bd474141f016aa66877989432d3a395b9308a443882461b45d77c40b2a7ea27c101b6e01466cf13ba436b5b6db4af7646b002f787863bcb73916daae3a33278adc37d0df863800389e247b92354f83212505c8071755fd9f4605afc7c16ec4d288142235ad3e924d9a4b8733706a871a9b2cd4bf0831e3da073384741a14a0989316bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a73a9eb1653c95320e39fe21a2685752af4a2355cd445f43d6f0dd028ad9bdf6a52ef531542e1b91d785df2458131ab155eae3b034c796d58064417101702a3061cb0e51ecea18879d02454382d65041f83faea2852882c4374fa085af9dff13c3e76377ab82853633d6cb4728e7cbb1387db9b67613ef26361fe4a1a5baab132d3380a112e6c4920050fea29d9820d1e7813fb4c6c144970097a9a6516059444661611459e9c9e73ffa97d33c3ed920807abf444b330424ec055d42e9818c86e23d8b84825961b027897c43ea2f394394a89043c2362c17e2b93ef18fb7c15618366b558fd3472509aec5771fae1b571b8e121280d5355090c85e11c894214668675ff1fedbf782641eae22da2f4eb630754fe0b5ad74c24f673452d2e2115777b73bb37f3cbb92f2aa8fc01b4c17852a971f311fefd63130f60537d3431b936a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a3d75da2b0862367e649c1b2cebcd7633633b965eda35ae474a66a7248337ed57d5600855c5a3b5601bb81c36a3c3b65c07ce2a25c44314280c5b36761f8dbb0ba7473432d2322e433b25423ae283266497619703912c9548fbe06f68e9f7cb465b22195ab2e2406960c42948713e741190ed0a3d14582f1ce2148e5839364e4da33b414e1033b5260f0ed302fbadd337ccf56828469fcb5860ef132e13fc281b8dc2dc69f9beb43027421279eedf6158445ac81021fc1b0cdc6ceb456b18ab17492ab97b6fcfb81e6c88ea62a486c34897d604320e89ea2c35204e5858d587062235ad3e924d9a4b8733706a871a9b2cd4bf0831e3da073384741a14a0989316bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a71db1e247133f01f55feee4802b22462f416241ad533201598723b30c9965c0845de1178a8622234f0e9e95a4555fa309973814d6fcdf91e5f104c30c126883b514bf50c60d3d94e0058a05b45f89b415055ed6bdefe0304c302cb2570e5a909b22a7e4eadd3aa201571705e1101f91aab68c60a75314a1b7906f839b69bdc1b6451ea4f9f3bc92e5765b052713ee743af7c3c650f1161153db7fb78eaee690601c89051821dd34d7c6e6078991a507e939c7674f88d02796c887c0a856a3f2581b9ca43397d8c1e5c4c1465cafa5b545a03445296934327ad87b85bd3477828119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a879f3957c78ec347bc71c0698dd7556e60e759070374a329e310cd36f216ad6e12a12f5f3db9d41b8f7e851957587024809368774743307d3f1b88257862815530bdac4d5b8299134825f774dca273696b48485217ed645a0ba7041d9337fa0a32f8273e07ff3a5390332b18d2c04c506da0bf4e46d97278428e84148bf5dc5d71e7c42f16e194718d9915024291cd2b08fba24d91e0e01451b60223b24877586568816e3dea950f6099503091e79e6f4391665b84d57413e8616177a0b7095e75dbd05aff48750aa891440acb61d65ab4891224f504660490db084746b0c814211fe3320b2ff515f7fee4253873e9328179f33838164b2631fcfe062cbdc476480afb1a9327de300961bf17f59ac955100e770ef504e867d22972217b17a4418e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4eb5e4b5281f389876d5cf0d641695a674e21b063876b9297b7fac423af1f05c2946612d64a3e10623f97d09079fdfdb29144d3e131bc81f02860d866d8e595b538180e9143f041d57824b4547ae2dbe17c9e2f70a048ef245613147473ec87c6d9203d1312357d00e031cba330dfaa118211c254ccffdb972125d7565bb69bf4960a1a7096453695ea838ab245f7a997929fab955f2655a09bbed3717b838ff53a49ae8291032bb6bc517e938fbee2f500698d03be315a30e8fa92a0a3704ce3f75dbd05aff48750aa891440acb61d65ab4891224f504660490db084746b0c814211fe3320b2ff515f7fee4253873e9328179f33838164b2631fcfe062cbdc476480afb1a9327de300961bf17f59ac955100e770ef504e867d22972217b17a4418e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e5d645e0cd82774250215cc572b4725023e03493202e0e46f9d6dd15d70abb00846039422e7c72a65a56a272e30158357a17631735a2ed447942701511c217b218180e9143f041d57824b4547ae2dbe17c9e2f70a048ef245613147473ec87c6d9203d1312357d00e031cba330dfaa118211c254ccffdb972125d7565bb69bf4960a1a7096453695ea838ab245f7a997929fab955f2655a09bbed3717b838ff53a49ae8291032bb6bc517e938fbee2f500698d03be315a30e8fa92a0a3704ce3f75dbd05aff48750aa891440acb61d65ab4891224f504660490db084746b0c814211fe3320b2ff515f7fee4253873e9328179f33838164b2631fcfe062cbdc476480afb1a9327de300961bf17f59ac955100e770ef504e867d22972217b17a4418e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e4e61ce0d47545b0f32fce474a8d7a311516ab77429071472cd5dfe2a29f26b02b73cec519c43f26bc1814b3159542b136bf62451b938ca30365b8147b56777421af54d142a05f6291d09fb2a953a9f54ee3e566fd2ca9c17ea112e0fb4c3877428501b7aa960461f2ff38d4f61876f667308f65d5cbf8e12aa0963084099180ab21d0e65336dbe43474baa36807ae35f03e22b6c1c526e7878a0ad5c6a54996143337354e94ef949ecb9ab5f16b22f0bafd45d6d69be491f1b71f50644ffc73353f7ec5d3d121a3520a6c261f714a26075c23d154145294a18344e2853cc315971625f218901fe6b94cf4c5030c3f65389fa2f2fd5c9210cd7a73e038f7ad957f9e86f5d77ad3a740cacd53353a7f5507163756a11e3cc60b3ee88592e890f5b8e882f2c2e80860c8b8c69708c397c2d0a16bc0c3d03220cbbd095508ceb8c393c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e9e7bf26ad007933934afc33e29490f2a96ea146ed8da2265f87ea31fd7b0311bff95817903cc1b4139e64923abab6233a924906e7892ce4077424b49e1f5f24fa13ce31d0f458e2b62c44b6c6254361c4a93ba79510e0c6a98574c52f8b85114b0851757baf5bd5c11fa72088cf58c7b4227e2502f846a69e7b6d1609eba341b5327481fbc80365fcb8d183432268a0a1da70229a2888b5d655568546da73c72aa1c3f616f32fb082667872aaedcfa795842300583a0607904934e3cc86a8c211afcc161744dde6805547d28c7af7a3c8939c3426fa58106f0b3842954efe12a119d13118f620e565d6fcc39acdc0a5ac9dc036bc9b8032e4399424106e08002bafc5159f2acb97cdb91b44139461338817c87249f54557b3a89e172954e9f2d78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21a26b81678595d7e58cb44955ff09dac6049a08553ba45016e0761ac768cc6f27a2067096e518c85623cdd945342aac66213d43a632d693b3b7609381ba5d46e459b1f250e96dadd2d42860e12e39f817805570f11a6fcd80ff4fe3d3f1bd2a72da36d3f701d560f1e96d4e13c31581a7552e6a87c83860f5ee18b7008fd4a080de04dd5058625a1788e75a924780140169d724454ebde6f4cdfd98802c85e5f44b430e44090c11f6606ad7b0658334d171b1ec34811f76a3e4fa44d5e67222d3b95d36d0c0effd27b5602b627c58e3077d3b25e2dd7f72d288dd22b1898869007c125a155e43d6472f4556d1f98fee81dd07a0c0826795e5b505ca63e6abd6804cf27ed4ea5613f566e84127698f678033b5c4e6efd0d1b6b8a02746143219e40ec09bf7421d27a49044f4307c8c0e936138edc3a1dc99b467a6b4650920cfc483c5401666b82de2354f1566e1eeae62a84df4c130247d65ab2c95478fd623f4e7de2fb351038b84b01651d005280fb747c9042464bc4e60b0741ba3a0f4f4b197f234d507b2b9959ce96026645cc1d4fe8d7061d11d4204097a5cb7737d03c236f799201554df21ec4d17c0ec4291970ac5d363ea313dc33e3383231909a2a228bf8f16e7c108a1c002c8b69527e3e0b8ba5f606416e9f292ee2390a0a65ca7583ba676991b56f6b96cd3c66f10d2c3faa1f770c700c603c92bb54110a9cf256c9fe903422a5bf152500527524c3c9145638da5d45afbc14ca22cf7973eef632fe02c64900512e4a8ce1896348a1b479dbc4422574fcce5ebb7f093b8de536072588447c81ff78325410f23f53c5061de1e2584e4b1ca72e17c8be696b255d5cf30e6f6ff455bc2fd70bab00f43ca701a9a3ee1ddc0cf42f6d3af176fced481e78df8d1e9105e0509d05ca2dfcf77e09cfd09c275e9b0d6f5d23d17a4d455308a3bbc551a5460704a46f7f78e03cf9625b177d39578cea6647068366b92cf21aa35f161700d4632113e592569229741da70a1b411c11606addd35f05c9e3286e6ffdf95f6e61a43c3623c305ac17e03c8d2f446e24306326e99b71265b5289782b74ba2753da4e56a90c495e73fae93363a8db38ab06f4543083bc41e3ddde1b726b88107753150156d70977ff1a93747b7f81673c8d84421c717b4d68b82b3a29c0f8078093113c007656792be91d0a94bd462ebea49f52d39de2796c6da30b6f46e94b42011e1721bc0d1a38d4ae2a05ec87270587603ffd1049286f5bf055fe4a122b1bf3e439fdb32679fe790e66d3559b195f7bfa5859095f028e25f120a893415521824b09e36b6c35c382c25adadb257d9eb7af3c528b0573340ca07632ac7e2bc7fbb80461961e5a7e66e515d37077448977ac71a368432f858bc43c94bf9447f084c74d8d89631e002f000100000000005b7cfe5668d23301be40a376f066ba5895438370a7e5ce525cfe51712b8d251a38a18e5de26632512a11e21ff95ebf19cf9a50473327fc4b" } } ] @@ -1303,10 +1303,10 @@ } }, "_info": { - "hash": "0x879f5976fdea34463877eebb31b5f5c7c966720d6a103273499e11d7dec42c05", + "hash": "0x3eaaca13f77ae9dea1073d43a01d42e15380997b00d12f5642b5474832e87f32", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_and_attester_signatures[fork_Devnet]", - "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - 3 validators in the genesis state\n - 2 additional attestations from validators 0 and 2 (in addition to proposer)\n - Verifies that all signatures are generated correctly\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n 2. Attester's signatures in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This test verifies multi-validator signature scenarios:\n - Multiple XMSS keys are generated for different validators\n - Attestations from non-proposer validators are correctly verified\n - Signature aggregation works with multiple attestations (signature positions are correct)", + "description": "Test valid proposer and attester signatures in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- 3 validators in the genesis state\n- Aggregated attestation from validators 0 and 2 (in addition to proposer)\n- Verifies that all signatures are generated and aggregated correctly\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n2. Aggregated attestation signatures can be verified against the validators'\n pubkeys in the state\n\nWhy This Matters\n----------------\nThis test verifies multi-validator signature aggregation:\n- Multiple XMSS keys are generated for different validators\n- Attestations with same data are properly aggregated\n- leanVM signature aggregation works with multiple validators", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json index 2847fbe..4967d3a 100644 --- a/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json +++ b/lean_client/test_vectors/verify_signatures/devnet/verify_signatures/test_valid_signatures/test_proposer_signature.json @@ -1264,7 +1264,7 @@ "hash": "0xfd453261ae39cec510a775702a42457e8aefbf018d4e32cad082b20bede7a8ae", "comment": "`leanSpec` generated test", "testId": "tests/consensus/devnet/verify_signatures/test_valid_signatures.py::test_proposer_signature[fork_Devnet]", - "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\n Scenario\n --------\n - Single block at slot 1\n - No additional attestations (only proposer attestation)\n\n Expected Behavior\n -----------------\n 1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\n Why This Matters\n ----------------\n This is the most basic signature generation test. It verifies:\n - XMSS key generation works\n - Signature aggregation includes proposer signature", + "description": "Test valid proposer signature in SignedBlockWithAttestation.\n\nScenario\n--------\n- Single block at slot 1\n- No additional attestations (only proposer attestation)\n\nExpected Behavior\n-----------------\n1. Proposer's signature in SignedBlockWithAttestation can be verified against\n the validator's pubkey in the state\n\nWhy This Matters\n----------------\nThis is the most basic signature generation test. It verifies:\n- XMSS key generation works\n- Signature aggregation includes proposer signature", "fixtureFormat": "verify_signatures_test" } } diff --git a/lean_client/validator/Cargo.toml b/lean_client/validator/Cargo.toml index f38a6d6..6c7152b 100644 --- a/lean_client/validator/Cargo.toml +++ b/lean_client/validator/Cargo.toml @@ -1,17 +1,16 @@ [package] name = "validator" -version = "0.1.0" -edition = "2021" - -[features] -default = ["xmss-signing"] -xmss-signing = ["leansig"] +edition = { workspace = true } [dependencies] -env-config = { path = "../env-config", default-features = false } -serde_yaml = "0.9" -containers = { path = "../containers" } -fork-choice = { path = "../fork_choice" } -tracing = "0.1" -typenum = "1.17" -leansig = { git = "https://github.com/leanEthereum/leanSig", branch = "main", optional = true } +anyhow = { workspace = true } +containers = { workspace = true } +env-config = { workspace = true } +fork_choice = { workspace = true } +serde_yaml = { workspace = true } +tracing = { workspace = true } +typenum = { workspace = true } +ethereum-types = { workspace = true } +ssz = { workspace = true } +xmss = { workspace = true } +zeroize = { workspace = true } diff --git a/lean_client/validator/src/keys.rs b/lean_client/validator/src/keys.rs index 7680102..87c5f58 100644 --- a/lean_client/validator/src/keys.rs +++ b/lean_client/validator/src/keys.rs @@ -1,36 +1,26 @@ -use containers::attestation::U3112; -use containers::ssz::ByteVector; -use containers::Signature; +use ssz::H256; use std::collections::HashMap; use std::path::{Path, PathBuf}; use tracing::info; -#[cfg(feature = "xmss-signing")] -use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8; -#[cfg(feature = "xmss-signing")] -use leansig::signature::SignatureScheme; -#[cfg(feature = "xmss-signing")] -use leansig::serialization::Serializable; - -#[cfg(not(feature = "xmss-signing"))] -use tracing::warn; +use anyhow::anyhow; +use anyhow::{Context, Result, ensure}; +use xmss::{SecretKey, Signature}; /// Manages XMSS secret keys for validators pub struct KeyManager { /// Map of validator index to secret key bytes - keys: HashMap>, + keys: HashMap, /// Path to keys directory keys_dir: PathBuf, } impl KeyManager { /// Load keys from the hash-sig-keys directory - pub fn new(keys_dir: impl AsRef) -> Result> { + pub fn new(keys_dir: impl AsRef) -> Result { let keys_dir = keys_dir.as_ref().to_path_buf(); - if !keys_dir.exists() { - return Err(format!("Keys directory not found: {:?}", keys_dir).into()); - } + ensure!(keys_dir.exists(), "Keys directory not found: {keys_dir:?}"); info!(path = ?keys_dir, "Initializing key manager"); @@ -41,16 +31,16 @@ impl KeyManager { } /// Load a secret key for a specific validator index - pub fn load_key(&mut self, validator_index: u64) -> Result<(), Box> { + pub fn load_key(&mut self, validator_index: u64) -> Result<()> { let sk_path = self .keys_dir .join(format!("validator_{}_sk.ssz", validator_index)); - if !sk_path.exists() { - return Err(format!("Secret key file not found: {:?}", sk_path).into()); - } + // todo(security): this probably should be zeroized + let key_bytes = std::fs::read(&sk_path) + .context(format!("Failed to read secret key file: {sk_path:?}"))?; - let key_bytes = std::fs::read(&sk_path)?; + let key = SecretKey::try_from(key_bytes.as_slice())?; info!( validator = validator_index, @@ -58,63 +48,18 @@ impl KeyManager { "Loaded secret key" ); - self.keys.insert(validator_index, key_bytes); + self.keys.insert(validator_index, key); Ok(()) } /// Sign a message with the validator's secret key - pub fn sign( - &self, - validator_index: u64, - epoch: u32, - message: &[u8; 32], - ) -> Result> { - #[cfg(feature = "xmss-signing")] - { - let key_bytes = self - .keys - .get(&validator_index) - .ok_or_else(|| format!("No key loaded for validator {}", validator_index))?; - - type SecretKey = - ::SecretKey; - - let secret_key = SecretKey::from_bytes(key_bytes) - .map_err(|e| format!("Failed to deserialize secret key: {:?}", e))?; - - let leansig_signature = - SIGTopLevelTargetSumLifetime32Dim64Base8::sign(&secret_key, epoch, message) - .map_err(|e| format!("Failed to sign message: {:?}", e))?; - - let sig_bytes = leansig_signature.to_bytes(); - - if sig_bytes.len() != 3112 { - return Err(format!( - "Invalid signature size: expected 3112, got {}", - sig_bytes.len() - ) - .into()); - } - - // Convert to ByteVector using unsafe pointer copy (same pattern as PublicKey) - let mut byte_vec: ByteVector = ByteVector::default(); - unsafe { - let dest = &mut byte_vec as *mut ByteVector as *mut u8; - std::ptr::copy_nonoverlapping(sig_bytes.as_ptr(), dest, 3112); - } - - Ok(byte_vec) - } - - #[cfg(not(feature = "xmss-signing"))] - { - let _ = (epoch, message); // Suppress unused warnings - warn!( - validator = validator_index, - "XMSS signing disabled - using zero signature" - ); - Ok(Signature::default()) - } + pub fn sign(&self, validator_index: u64, epoch: u32, message: H256) -> Result { + let key = self + .keys + .get(&validator_index) + .ok_or_else(|| anyhow!("No key loaded for validator {}", validator_index))?; + + key.sign(message, epoch) } /// Check if a key is loaded for a validator diff --git a/lean_client/validator/src/lib.rs b/lean_client/validator/src/lib.rs index adb17fb..63a9d0e 100644 --- a/lean_client/validator/src/lib.rs +++ b/lean_client/validator/src/lib.rs @@ -2,21 +2,20 @@ use std::collections::HashMap; use std::path::Path; -use containers::block::BlockSignatures; -use containers::ssz; +use anyhow::{Context, Result, anyhow, bail, ensure}; use containers::{ - attestation::{Attestation, AttestationData, Signature, SignedAttestation}, - block::{hash_tree_root, BlockWithAttestation, SignedBlockWithAttestation}, - checkpoint::Checkpoint, - types::{Uint64, ValidatorIndex}, - Slot, + Attestation, AttestationData, BlockSignatures, BlockWithAttestation, Checkpoint, + SignedAttestation, SignedBlockWithAttestation, Slot, }; -use fork_choice::store::{get_proposal_head, get_vote_target, Store}; +use ethereum_types::H256; +use fork_choice::store::{Store, get_proposal_head, get_vote_target}; +use ssz::SszHash; use tracing::{info, warn}; pub mod keys; use keys::KeyManager; +use xmss::Signature; pub type ValidatorRegistry = HashMap>; // Node @@ -101,14 +100,14 @@ impl ValidatorService { }) } - pub fn get_proposer_for_slot(&self, slot: Slot) -> Option { + pub fn get_proposer_for_slot(&self, slot: Slot) -> Option { if self.num_validators == 0 { return None; } let proposer = slot.0 % self.num_validators; if self.config.is_assigned(proposer) { - Some(ValidatorIndex(proposer)) + Some(proposer) } else { None } @@ -119,49 +118,49 @@ impl ValidatorService { &self, store: &mut Store, slot: Slot, - proposer_index: ValidatorIndex, - ) -> Result { + proposer_index: u64, + ) -> Result { info!( slot = slot.0, - proposer = proposer_index.0, + proposer = proposer_index, "Building block proposal" ); let parent_root = get_proposal_head(store, slot); info!( - parent_root = %format!("0x{:x}", parent_root.0), - store_head = %format!("0x{:x}", store.head.0), + parent_root = %format!("0x{:x}", parent_root), + store_head = %format!("0x{:x}", store.head), "Using parent root for block proposal" ); let parent_state = store .states .get(&parent_root) - .ok_or_else(|| format!("Couldn't find parent state {:?}", parent_root))?; + .ok_or_else(|| anyhow!("Couldn't find parent state {:?}", parent_root))?; let vote_target = get_vote_target(store); // Validate that target slot is greater than or equal to source slot // At genesis, both target and source are slot 0, which is valid - if vote_target.slot < store.latest_justified.slot { - return Err(format!( - "Invalid attestation: target slot {} must be >= source slot {}", - vote_target.slot.0, store.latest_justified.slot.0 - )); - } + ensure!( + vote_target.slot >= store.latest_justified.slot, + "Invalid attestation: target slot {} must be >= source slot {}", + vote_target.slot.0, + store.latest_justified.slot.0 + ); let head_block = store .blocks .get(&store.head) - .ok_or("Head block not found")?; + .ok_or(anyhow!("Head block not found"))?; let head_checkpoint = Checkpoint { root: store.head, slot: head_block.slot, }; let proposer_attestation = Attestation { - validator_id: Uint64(proposer_index.0), + validator_id: proposer_index, data: AttestationData { slot, head: head_checkpoint, @@ -221,7 +220,7 @@ impl ValidatorService { && !target_already_justified }) .map(|(validator_idx, data)| Attestation { - validator_id: Uint64(validator_idx.0), + validator_id: *validator_idx, data: data.clone(), }) .collect(); @@ -244,10 +243,9 @@ impl ValidatorService { .into_iter() .flat_map(|(_, slot_atts)| { // Group by data root (Bytes32 implements Hash) - let mut data_groups: HashMap> = - HashMap::new(); + let mut data_groups: HashMap> = HashMap::new(); for att in slot_atts { - let data_root = att.data.data_root_bytes(); + let data_root = att.data.hash_tree_root(); data_groups.entry(data_root).or_default().push(att); } // Find the data with the most attestations @@ -288,9 +286,9 @@ impl ValidatorService { info!( slot = block.slot.0, - proposer = block.proposer_index.0, - parent_root = %format!("0x{:x}", block.parent_root.0), - state_root = %format!("0x{:x}", block.state_root.0), + proposer = block.proposer_index, + parent_root = %format!("0x{:x}", block.parent_root), + state_root = %format!("0x{:x}", block.state_root), attestation_sigs = num_attestations, "Block built successfully" ); @@ -300,16 +298,16 @@ impl ValidatorService { if let Some(ref key_manager) = self.key_manager { // Sign proposer attestation with XMSS - let message = hash_tree_root(&proposer_attestation); + let message = proposer_attestation.hash_tree_root(); let epoch = slot.0 as u32; - match key_manager.sign(proposer_index.0, epoch, &message.0.into()) { + match key_manager.sign(proposer_index, epoch, message) { Ok(sig) => { proposer_signature = sig; - info!(proposer = proposer_index.0, "Signed proposer attestation"); + info!(proposer = proposer_index, "Signed proposer attestation"); } Err(e) => { - return Err(format!("Failed to sign proposer attestation: {}", e)); + bail!("Failed to sign proposer attestation: {}", e); } } } else { @@ -324,7 +322,7 @@ impl ValidatorService { let mut list = ssz::PersistentList::default(); for proof in signatures { list.push(proof) - .map_err(|e| format!("Failed to add attestation signature: {:?}", e))?; + .context("Failed to add attestation signature")?; } list }; @@ -384,10 +382,10 @@ impl ValidatorService { let signature = if let Some(ref key_manager) = self.key_manager { // Sign with XMSS - let message = hash_tree_root(&attestation); + let message = attestation.hash_tree_root(); let epoch = slot.0 as u32; - match key_manager.sign(idx, epoch, &message.0.into()) { + match key_manager.sign(idx, epoch, message) { Ok(sig) => { info!( slot = slot.0, diff --git a/lean_client/xmss/Cargo.toml b/lean_client/xmss/Cargo.toml new file mode 100644 index 0000000..dd85368 --- /dev/null +++ b/lean_client/xmss/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "xmss" +edition = { workspace = true } + +[lints] +workspace = true + +[dependencies] +anyhow = { workspace = true } +derive_more = { workspace = true } +eth_ssz = { workspace = true } +ethereum-types = { workspace = true } +hex = { workspace = true } +lean-multisig = { workspace = true } +leansig = { workspace = true } +rand = { workspace = true } +ssz = { workspace = true } +typenum = { workspace = true } +serde = { workspace = true } +zeroize = { workspace = true, features = ["derive"] } + +[dev-dependencies] +rand_chacha = { workspace = true } \ No newline at end of file diff --git a/lean_client/xmss/src/aggregated_signature.rs b/lean_client/xmss/src/aggregated_signature.rs new file mode 100644 index 0000000..28c9825 --- /dev/null +++ b/lean_client/xmss/src/aggregated_signature.rs @@ -0,0 +1,163 @@ +use core::fmt::{self, Display}; +use std::{str::FromStr, sync::Once}; + +use crate::{PublicKey, Signature}; +use anyhow::{Context, Error, Result, anyhow, bail}; +use eth_ssz::{Decode, Encode}; +use ethereum_types::H256; +use lean_multisig::{ + Devnet2XmssAggregateSignature, xmss_aggregate_signatures, xmss_aggregation_setup_prover, + xmss_aggregation_setup_verifier, xmss_verify_aggregated_signatures, +}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; +use ssz::{ByteList, Ssz}; +use typenum::U1048576; + +/// Max size currently is 1MiB by spec. +type AggregatedSignatureSizeLimit = U1048576; + +/// Cryptographic proof that a set of validators signed a message. +/// +/// Note: this doesn't follow spec a bit - in spec this would be a `proof_data` +/// field of AggregatedSignatureProof type. Implemented like this to have a +/// bit of nice encapsulation, so that xmss-related types don't leak +/// abstraction into containers crate. +/// +/// todo(xmss): deriving Ssz not particularly good there, as this won't validate +/// if it actually has valid proof structure, so `.as_lean()` method may panic. +#[derive(Debug, Clone, Ssz)] +pub struct AggregatedSignature(ByteList); + +fn setup_prover() { + static PROVER_SETUP: Once = Once::new(); + + PROVER_SETUP.call_once(|| xmss_aggregation_setup_prover()); +} + +fn setup_verifier() { + static VERIFIER_SETUP: Once = Once::new(); + + VERIFIER_SETUP.call_once(|| xmss_aggregation_setup_verifier()); +} + +impl AggregatedSignature { + pub fn new(bytes: &[u8]) -> Result { + let bytes = ByteList::try_from(bytes.to_vec()) + .context("signature too large - currently max 1MiB signatures allowed")?; + + Devnet2XmssAggregateSignature::from_ssz_bytes(bytes.as_bytes()) + .map_err(|err| anyhow!("{err:?}")) + .context("invalid aggregated signature")?; + + Ok(Self(bytes)) + } + + pub fn aggregate( + public_keys: impl IntoIterator, + signatures: impl IntoIterator, + message: H256, + epoch: u32, + ) -> Result { + setup_prover(); + + let public_keys = public_keys + .into_iter() + .map(|k| k.as_lean()) + .collect::>(); + let signatures = signatures + .into_iter() + .map(|s| s.as_lean()) + .collect::>(); + + if public_keys.len() != signatures.len() { + bail!( + "public key & signature count mismatch ({} != {})", + public_keys.len(), + signatures.len() + ); + } + + let aggregate = + xmss_aggregate_signatures(&public_keys, &signatures, message.as_fixed_bytes(), epoch) + .map_err(|err| anyhow!("{err:?}"))?; + + Ok(Self(aggregate.as_ssz_bytes().try_into()?)) + } + + pub fn verify( + &self, + public_keys: impl IntoIterator, + message: H256, + epoch: u32, + ) -> Result<()> { + setup_verifier(); + + let public_keys = public_keys + .into_iter() + .map(|k| k.as_lean()) + .collect::>(); + + let aggregated_signature = self.as_lean(); + + xmss_verify_aggregated_signatures( + &public_keys, + message.as_fixed_bytes(), + &aggregated_signature, + epoch, + ) + .map_err(|err| anyhow!("{err:?}")) + } + + fn as_lean(&self) -> Devnet2XmssAggregateSignature { + Devnet2XmssAggregateSignature::from_ssz_bytes(self.0.as_bytes()) + .expect("AggregatedSignature was not constructed properly") + } + + // todo(xmss): this is a function used only for testing. ideally, it should not exist + pub fn is_empty(&self) -> bool { + self.0.as_bytes().is_empty() + } +} + +impl Display for AggregatedSignature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0.as_bytes())) + } +} + +impl FromStr for AggregatedSignature { + type Err = Error; + + fn from_str(s: &str) -> Result { + let data = s.strip_prefix("0x").unwrap_or(s); + + let bytes = hex::decode(data)?; + + Self::new(&bytes).map_err(|err| anyhow!("{err:?}")) + } +} + +impl Serialize for AggregatedSignature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.to_string().as_str()) + } +} + +impl<'de> Deserialize<'de> for AggregatedSignature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct DataWrapper { + data: String, + } + + let value = DataWrapper::deserialize(deserializer)?; + + value.data.parse().map_err(de::Error::custom) + } +} diff --git a/lean_client/xmss/src/lib.rs b/lean_client/xmss/src/lib.rs new file mode 100644 index 0000000..6277fe2 --- /dev/null +++ b/lean_client/xmss/src/lib.rs @@ -0,0 +1,9 @@ +mod aggregated_signature; +mod public_key; +mod secret_key; +mod signature; + +pub use aggregated_signature::AggregatedSignature; +pub use public_key::PublicKey; +pub use secret_key::SecretKey; +pub use signature::Signature; diff --git a/lean_client/xmss/src/public_key.rs b/lean_client/xmss/src/public_key.rs new file mode 100644 index 0000000..fce7629 --- /dev/null +++ b/lean_client/xmss/src/public_key.rs @@ -0,0 +1,118 @@ +use core::{ + convert::{TryFrom, TryInto}, + fmt::{self, Debug, Display}, + str::FromStr, +}; + +use anyhow::{Error, anyhow}; +use leansig::{serialization::Serializable, signature::SignatureScheme, signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8}; +use serde::{Deserialize, Serialize, de::{self, Visitor}}; +use ssz::{ByteVector, Ssz}; +use eth_ssz::DecodeError; +use typenum::U52; + +type PublicKeySize = U52; + +type LeanSigPublicKey = ::PublicKey; + +// todo(xmss): default implementation doesn't make sense here +#[derive(Clone, Ssz, Default, PartialEq, Eq)] +pub struct PublicKey(ByteVector); + +impl PublicKey { + pub fn new(bytes: &[u8]) -> Result { + LeanSigPublicKey::from_bytes(bytes)?; + + Ok(Self(bytes.try_into().expect( + "slice of length != 52 shouldn't deserialize as valid leansig public key", + ))) + } + + pub(crate) fn from_lean(key: LeanSigPublicKey) -> Self { + Self( + key.to_bytes() + .as_slice() + .try_into() + .expect("slice of length != 52 shouldn't deserialize as valid leansig public key"), + ) + } + + pub(crate) fn as_lean(&self) -> LeanSigPublicKey { + LeanSigPublicKey::from_bytes(self.0.as_bytes()) + .expect("PublicKey was instantiated incorrectly") + } +} + +impl Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0.as_bytes())) + } +} + +impl Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0.as_bytes())) + } +} + +impl FromStr for PublicKey { + type Err = Error; + + fn from_str(s: &str) -> Result { + let data = s.strip_prefix("0x").unwrap_or(s); + + let bytes = hex::decode(data)?; + + Self::new(&bytes).map_err(|err| anyhow!("{err:?}")) + } +} + +impl TryFrom<&[u8]> for PublicKey { + type Error = DecodeError; + + fn try_from(value: &[u8]) -> Result { + Self::new(value) + } +} + +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.to_string().as_str()) + } +} + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct SignatureVisitor; + + impl Visitor<'_> for SignatureVisitor { + type Value = PublicKey; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "public key") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + v.parse().map_err(de::Error::custom) + } + + fn visit_string(self, v: String) -> Result + where + E: de::Error, + { + self.visit_str(&v) + } + } + + deserializer.deserialize_str(SignatureVisitor) + } +} diff --git a/lean_client/xmss/src/secret_key.rs b/lean_client/xmss/src/secret_key.rs new file mode 100644 index 0000000..f8b2b8e --- /dev/null +++ b/lean_client/xmss/src/secret_key.rs @@ -0,0 +1,66 @@ +use anyhow::{Context, Error, Result, anyhow}; +use derive_more::Debug; +use leansig::{serialization::Serializable, signature::{ + SignatureScheme, generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8 +}}; +use rand::Rng; +use ssz::H256; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +use crate::{PublicKey, Signature}; + +type LeanSigSecretKey = ::SecretKey; + +#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug)] +#[debug("[REDACTED]")] +pub struct SecretKey(Vec); + +impl SecretKey { + pub fn sign(&self, message: H256, epoch: u32) -> Result { + let signature = ::sign( + &self.as_lean(), + epoch, + message.as_fixed_bytes(), + ) + .context("failed to sign message")?; + + Ok(Signature::from_lean(signature)) + } + + pub fn generate_key_pair( + rng: &mut R, + activation_epoch: u32, + num_active_epochs: u32, + ) -> (PublicKey, SecretKey) { + let (public_key, secret_key) = + ::key_gen::( + rng, + activation_epoch as usize, + num_active_epochs as usize, + ); + + ( + PublicKey::from_lean(public_key), + SecretKey::from_lean(secret_key), + ) + } + + fn from_lean(key: LeanSigSecretKey) -> Self { + Self(key.to_bytes()) + } + + fn as_lean(&self) -> LeanSigSecretKey { + LeanSigSecretKey::from_bytes(&self.0).expect("SecretKey was instantiated incorrectly") + } +} + +impl TryFrom<&[u8]> for SecretKey { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + LeanSigSecretKey::from_bytes(value) + .map_err(|_| anyhow!("value is not valid secret key"))?; + + Ok(Self(value.to_vec())) + } +} diff --git a/lean_client/xmss/src/signature.rs b/lean_client/xmss/src/signature.rs new file mode 100644 index 0000000..3467ece --- /dev/null +++ b/lean_client/xmss/src/signature.rs @@ -0,0 +1,202 @@ +use core::{ + convert::TryFrom, + fmt::{self, Debug, Display}, + str::FromStr, +}; + +use anyhow::{Error, anyhow, Result}; +use eth_ssz::DecodeError; +use leansig::{serialization::Serializable, signature::SignatureScheme}; +use leansig::signature::generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8; +use serde::de; +use serde::{Deserialize, Serialize}; +use ssz::{ByteVector, H256, Ssz}; +use crate::public_key::PublicKey; +use typenum::{Diff, U984, U4096}; + +type U3112 = Diff; + +type SignatureSize = U3112; + +type LeanSigSignature = ::Signature; + +// todo(xmss): default implementation doesn't make sense here, and is needed only for tests +#[derive(Ssz, Clone, Default)] +pub struct Signature(ByteVector); + +impl Signature { + pub fn new(inner: &[u8]) -> Result { + LeanSigSignature::from_bytes(inner)?; + + Ok(Self(inner.try_into().expect( + "slice of length != 3112 shouldn't deserialize as valid leansig signature", + ))) + } + + pub fn verify(&self, public_key: &PublicKey, epoch: u32, message: H256) -> Result<()> { + let is_valid = ::verify( + &public_key.as_lean(), + epoch, + message.as_fixed_bytes(), + &self.as_lean(), + ); + + is_valid.then_some(()).ok_or(anyhow!("invalid signature")) + } + + pub(crate) fn from_lean(signature: LeanSigSignature) -> Self { + let bytes = signature.to_bytes(); + + Self( + bytes + .as_slice() + .try_into() + .expect("slice of length != 3112 shouldn't deserialize as valid leansig signature"), + ) + } + + pub(crate) fn as_lean(&self) -> LeanSigSignature { + LeanSigSignature::from_bytes(self.0.as_bytes()) + .expect("signature internal representation must be valid leansig signature") + } +} + +impl Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0.as_bytes())) + } +} + +impl Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0.as_bytes())) + } +} + +impl FromStr for Signature { + type Err = Error; + + fn from_str(s: &str) -> Result { + let data = s.strip_prefix("0x").unwrap_or(s); + + let bytes = hex::decode(data)?; + + Self::new(&bytes).map_err(|err| anyhow!("{err:?}")) + } +} + +impl TryFrom<&[u8]> for Signature { + type Error = DecodeError; + + fn try_from(value: &[u8]) -> Result { + Self::new(value) + } +} + +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.to_string().as_str()) + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct DataWrapper { + data: T, + } + + #[derive(Deserialize)] + struct XmssSignature { + path: XmssPath, + rho: DataWrapper>, + hashes: DataWrapper>>>, + } + + #[derive(Deserialize)] + struct XmssPath { + siblings: DataWrapper>>>, + } + + let xmss_sig = XmssSignature::deserialize(deserializer)?; + let mut rho_bytes = Vec::new(); + for val in &xmss_sig.rho.data { + rho_bytes.extend_from_slice(&val.to_le_bytes()); + } + let rho_len = rho_bytes.len(); // Should be 28 (7 * 4) + + // 2. Serialize Path/Siblings (Variable length) + let mut path_bytes = Vec::new(); + // Prepend 4 bytes (containing 4) as an offset which would come with real SSZ serialization + let inner_offset: u32 = 4; + path_bytes.extend_from_slice(&inner_offset.to_le_bytes()); // [04 00 00 00] + for sibling in &xmss_sig.path.siblings.data { + for val in &sibling.data { + path_bytes.extend_from_slice(&val.to_le_bytes()); + } + } + + // 3. Serialize Hashes (Variable length) + let mut hashes_bytes = Vec::new(); + for hash in &xmss_sig.hashes.data { + for val in &hash.data { + hashes_bytes.extend_from_slice(&val.to_le_bytes()); + } + } + + // --- STEP 2: CALCULATE OFFSETS --- + + // The fixed part contains: + // 1. Path Offset (4 bytes) + // 2. Rho Data (rho_len bytes) + // 3. Hashes Offset (4 bytes) + let fixed_part_size = 4 + rho_len + 4; + + // Offset to 'path' starts immediately after the fixed part + let offset_path = fixed_part_size as u32; + + // Offset to 'hashes' starts after 'path' data + let offset_hashes = offset_path + (path_bytes.len() as u32); + + // --- STEP 3: CONSTRUCT FINAL SSZ BYTES --- + + let mut ssz_bytes = Vec::new(); + + // 1. Write Offset to Path (u32, Little Endian) + ssz_bytes.extend_from_slice(&offset_path.to_le_bytes()); + + // 2. Write Rho Data (Fixed) + ssz_bytes.extend_from_slice(&rho_bytes); + + // 3. Write Offset to Hashes (u32, Little Endian) + ssz_bytes.extend_from_slice(&offset_hashes.to_le_bytes()); + + // 4. Write Path Data (Variable) + ssz_bytes.extend_from_slice(&path_bytes); + + // 5. Write Hashes Data (Variable) + ssz_bytes.extend_from_slice(&hashes_bytes); + + println!("Total SSZ Bytes Length: {}", ssz_bytes.len()); + + Signature::try_from(ssz_bytes.as_slice()) + .map_err(|err| de::Error::custom(format!("invalid signature: {err:?}"))) + } +} + +#[cfg(test)] +mod test { + use crate::signature::SignatureSize; + use typenum::Unsigned; + + #[test] + fn valid_signature_size() { + assert_eq!(SignatureSize::U64, 3112); + } +} diff --git a/lean_client/xmss/tests/aggregate.rs b/lean_client/xmss/tests/aggregate.rs new file mode 100644 index 0000000..457cef6 --- /dev/null +++ b/lean_client/xmss/tests/aggregate.rs @@ -0,0 +1,28 @@ +use rand::SeedableRng; +use rand_chacha::ChaCha8Rng; +use ssz::H256; +use xmss::{AggregatedSignature, SecretKey}; + +#[test] +fn aggregate_two() { + let mut rng = ChaCha8Rng::seed_from_u64(7); + let (public_key1, secret_key1) = SecretKey::generate_key_pair(&mut rng, 0, 1); + let (public_key2, secret_key2) = SecretKey::generate_key_pair(&mut rng, 0, 1); + + let message = H256([1; 32]); + + let sig1 = secret_key1.sign(message.clone(), 0).unwrap(); + let sig2 = secret_key2.sign(message.clone(), 0).unwrap(); + + let aggr_sig = AggregatedSignature::aggregate( + vec![public_key1.clone(), public_key2.clone()], + vec![sig1, sig2], + message.clone(), + 0, + ) + .unwrap(); + + aggr_sig + .verify(vec![public_key1, public_key2], message, 0) + .unwrap(); +} diff --git a/lean_client/xmss/tests/public_key.rs b/lean_client/xmss/tests/public_key.rs new file mode 100644 index 0000000..2235346 --- /dev/null +++ b/lean_client/xmss/tests/public_key.rs @@ -0,0 +1,15 @@ +use rand::SeedableRng; +use rand_chacha::ChaCha8Rng; +use xmss::{PublicKey, SecretKey}; + +#[test] +pub fn display_parse_roundtrip() { + let mut rng = ChaCha8Rng::seed_from_u64(0); + let (public_key, _) = SecretKey::generate_key_pair(&mut rng, 0, 1); + + let str = public_key.to_string(); + let parsed = str.parse::(); + + assert!(parsed.is_ok()); + assert_eq!(parsed.unwrap(), public_key); +} diff --git a/lean_client/xmss/tests/signing.rs b/lean_client/xmss/tests/signing.rs new file mode 100644 index 0000000..c250707 --- /dev/null +++ b/lean_client/xmss/tests/signing.rs @@ -0,0 +1,14 @@ +use rand::SeedableRng; +use rand_chacha::ChaCha8Rng; +use ssz::H256; +use xmss::SecretKey; + +#[test] +pub fn sign_verify_roundtrip() { + let mut rng = ChaCha8Rng::seed_from_u64(0); + let (public_key, private_key) = SecretKey::generate_key_pair(&mut rng, 0, 1); + + let sig = private_key.sign(H256::zero(), 0).unwrap(); + + sig.verify(&public_key, 0, H256::zero()).unwrap(); +} From 7bf11a233735d074da4f5136eba28533cf97bae3 Mon Sep 17 00:00:00 2001 From: artiomtr <44021713+ArtiomTr@users.noreply.github.com> Date: Mon, 2 Feb 2026 13:24:14 +0200 Subject: [PATCH 48/48] Set default devnet tag to `devnet-2` Preparing to merge into main branch. In order to build `devnet-2` tag during latest branch build, need to update reference in Makefile. --- lean_client/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lean_client/Makefile b/lean_client/Makefile index cf4dc05..9072046 100644 --- a/lean_client/Makefile +++ b/lean_client/Makefile @@ -16,7 +16,7 @@ GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) # Build date. Used for docker image metadata. BUILD_DATE := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') # Currently supported devnet. Used to publish devnet tag, along with `latest`. -CURRENT_DEVNET := devnet-1 +CURRENT_DEVNET := devnet-2 # Temporary variable, which constructs `--tag` arguments, to be passed into # docker build command. It is used as intermediatery step, not as config